[Libguestfs] [PATCH 9/9] Rust bindings: Complete bindings

Hiroyuki Katsura hiroyuki.katsura.0513 at gmail.com
Thu Jun 27 08:06:10 UTC 2019


From: Hiroyuki_Katsura <hiroyuki.katsura.0513 at gmail.com>

---
 configure.ac                        |   2 +
 generator/bindtests.ml              |  63 +++++
 generator/main.ml                   |   2 +-
 generator/rust.ml                   | 423 +++++++++++++++++-----------
 generator/rust.mli                  |   5 +-
 rust/.gitignore                     |   2 +
 rust/Makefile.am                    |  16 +-
 rust/run-bindtests                  |  23 ++
 rust/run-tests                      |  21 ++
 rust/src/bin/.gitkeep               |   0
 rust/tests/010_load.rs              |  28 +-
 rust/tests/020_create.rs            |  28 +-
 rust/tests/030_create_flags.rs      |  29 +-
 rust/tests/040_create_multiple.rs   |  28 +-
 rust/tests/050_handle_properties.rs |  31 +-
 rust/tests/070_opt_args.rs          |  39 +--
 rust/tests/080_version.rs           |  31 +-
 rust/tests/090_ret_values.rs        |  61 ++++
 rust/tests/100_launch.rs            |  65 +++++
 19 files changed, 618 insertions(+), 279 deletions(-)
 create mode 100644 rust/.gitignore
 create mode 100755 rust/run-bindtests
 create mode 100755 rust/run-tests
 create mode 100644 rust/src/bin/.gitkeep
 create mode 100644 rust/tests/090_ret_values.rs
 create mode 100644 rust/tests/100_launch.rs

diff --git a/configure.ac b/configure.ac
index f9bdbe54b..b35b1ce0f 100644
--- a/configure.ac
+++ b/configure.ac
@@ -431,6 +431,8 @@ AS_ECHO_N(["Vala bindings ....................... "])
 if test "x$ENABLE_VAPIGEN_TRUE" = "x"; then echo "yes"; else echo "no"; fi
 AS_ECHO_N(["bash completion ..................... "])
 if test "x$HAVE_BASH_COMPLETION_TRUE" = "x"; then echo "yes"; else echo "no"; fi
+AS_ECHO_N(["Rust bindings ....................... "])
+if test "x$HAVE_RUST_TRUE" = "x"; then echo "yes"; else echo "no"; fi
 echo
 echo "If any optional component is configured 'no' when you expected 'yes'"
 echo "then you should check the preceding messages."
diff --git a/generator/bindtests.ml b/generator/bindtests.ml
index 41aef47ea..e88e71c8a 100644
--- a/generator/bindtests.ml
+++ b/generator/bindtests.ml
@@ -986,6 +986,69 @@ and generate_php_bindtests () =
 and generate_rust_bindtests () =
   generate_header CStyle GPLv2plus;
 
+  pr "extern crate guestfs;\n";
+  pr "use guestfs::*;\n";
+  pr "use std::default::Default;\n";
+  pr "\n";
+  pr "fn main() {\n";
+  pr "    let g = match Handle::create() {\n";
+  pr "        Ok(g) => g,\n";
+  pr "        Err(e) => panic!(format!(\" could not create handle {}\", e)),\n";
+  pr "    };\n";
+  generate_lang_bindtests (
+    fun f args optargs ->
+      pr "    g.%s(" f;
+      let needs_comma = ref false in
+      List.iter (
+        fun arg ->
+          if !needs_comma then pr ", ";
+          needs_comma := true;
+          match arg with
+          | CallString s -> pr "\"%s\"" s
+          | CallOptString None -> pr "None"
+          | CallOptString (Some s) -> pr "Some(\"%s\")" s
+          | CallStringList xs ->
+            pr "&vec![%s]"
+              (String.concat ", " (List.map (sprintf "\"%s\"") xs))
+          | CallInt i -> pr "%d" i
+          | CallInt64 i -> pr "%Ldi64" i
+          | CallBool b -> pr "%b" b
+          | CallBuffer s ->
+            let f = fun x -> sprintf "%d" (Char.code x) in
+            pr "&[%s]"
+              (String.concat ", " (List.map f (String.explode s)))
+      ) args;
+      if !needs_comma then pr ", ";
+      (match optargs with
+       | None -> pr "Default::default()"
+       | Some optargs ->
+         pr "%sOptArgs{" (Rust.snake2caml f);
+         needs_comma := false;
+         List.iter (
+           fun optarg ->
+             if !needs_comma then pr ", ";
+             needs_comma := true;
+             match optarg with
+             | CallOBool (n, v) ->
+               pr "%s: Some(%b)" n v
+             | CallOInt (n, v) ->
+               pr "%s: Some(%d)" n v
+             | CallOInt64 (n, v) ->
+               pr "%s: Some(%Ldi64)" n v
+             | CallOString (n, v) ->
+               pr "%s: Some(\"%s\")" n v
+             | CallOStringList (n, xs) ->
+               pr "%s: Some(&[%s])"
+                 n (String.concat ", " (List.map (sprintf "\"%s\"") xs))
+         ) optargs;
+         if !needs_comma then pr ", ";
+         pr ".. Default::default()}";
+      );
+      pr ").expect(\"failed to run\");\n";
+  );
+  pr "    println!(\"EOF\");\n";
+  pr "}\n";
+
 (* Language-independent bindings tests - we do it this way to
  * ensure there is parity in testing bindings across all languages.
  *)
diff --git a/generator/main.ml b/generator/main.ml
index 5de89138c..ffe1bb95c 100644
--- a/generator/main.ml
+++ b/generator/main.ml
@@ -374,7 +374,7 @@ Run it from the top source directory using the command
 
   output_to "rust/src/lib.rs"
             Rust.generate_rust;
-  output_to "rust/tests/bind_test.rs"
+  output_to "rust/src/bin/bindtests.rs"
             Bindtests.generate_rust_bindtests;
 
   (* Generate the list of files generated -- last. *)
diff --git a/generator/rust.ml b/generator/rust.ml
index 4766357be..ee65b1073 100644
--- a/generator/rust.ml
+++ b/generator/rust.ml
@@ -61,14 +61,16 @@ let generate_rust () =
   pr "
 use std::collections;
 use std::convert;
+use std::convert::TryFrom;
 use std::ffi;
-use std::slice;
-use std::ptr;
 use std::os::raw::{c_char, c_int, c_void};
+use std::ptr;
+use std::slice;
+use std::str;
 
 
 #[allow(non_camel_case_types)]
-enum guestfs_h {}  // opaque struct
+enum guestfs_h {} // opaque struct
 
 #[link(name = \"guestfs\")]
 extern \"C\" {
@@ -141,22 +143,22 @@ impl CreateFlags {
 }
 
 struct NullTerminatedIter<T: Copy + Clone> {
-    p: *const T
+    p: *const *const T,
 }
 
 impl<T: Copy + Clone> NullTerminatedIter<T> {
-    fn new(p: *const T) -> NullTerminatedIter<T> {
-        NullTerminatedIter{ p }
+    fn new(p: *const *const T) -> NullTerminatedIter<T> {
+        NullTerminatedIter { p }
     }
 }
 
 impl<T: Copy + Clone> Iterator for NullTerminatedIter<T> {
-    type Item = T;
-    fn next(&mut self) -> Option<T> {
-        if self.p.is_null() {
+    type Item = *const T;
+    fn next(&mut self) -> Option<*const T> {
+        let r = unsafe { *(self.p) };
+        if r.is_null() {
             None
         } else {
-            let r = unsafe { *(self.p) };
             self.p = unsafe { self.p.offset(1) };
             Some(r)
         }
@@ -171,12 +173,15 @@ struct RawList<T> {
 
 struct RawListIter<'a, T> {
     current: u32,
-    list: &'a RawList<T>
+    list: &'a RawList<T>,
 }
 
-impl<T> RawList<T>  {
+impl<T> RawList<T> {
     fn iter<'a>(&'a self) -> RawListIter<'a, T> {
-        RawListIter{ current: 0, list: self }
+        RawListIter {
+            current: 0,
+            list: self,
+        }
     }
 }
 
@@ -193,60 +198,81 @@ impl<'a, T> Iterator for RawListIter<'a, T> {
     }
 }
 
-
-fn arg_string_list (v: &Vec<&str>) -> Vec<*const i8> {
-    let length = v.len();
+fn arg_string_list(v: &[&str]) -> Result<Vec<ffi::CString>, Error> {
     let mut w = Vec::new();
     for x in v.iter() {
         let y: &str = x;
-        let s = ffi::CString::new(y).unwrap();
-        w.push(s.as_ptr());
+        w.push(ffi::CString::new(y)?);
+    }
+    Ok(w)
+}
+
+fn free_string_list(l: *const *const c_char) {
+    for buf in NullTerminatedIter::new(l) {
+        unsafe { free(buf as * const c_void) };
     }
-    w.push(ptr::null());
-    w
+    unsafe { free(l as *const c_void) };
 }
 
-fn hashmap (l: *const *const c_char) -> collections::HashMap<String, String> {
+fn hashmap(l: *const *const c_char) -> Result<collections::HashMap<String, String>, Error> {
     let mut map = collections::HashMap::new();
     let mut iter = NullTerminatedIter::new(l);
     while let Some(key) = iter.next() {
         if let Some(val) = iter.next() {
-            let key = unsafe { ffi::CStr::from_ptr(key) }.to_str().unwrap();
-            let val = unsafe { ffi::CStr::from_ptr(val) }.to_str().unwrap();
+            let key = unsafe { ffi::CStr::from_ptr(key) }.to_str()?;
+            let val = unsafe { ffi::CStr::from_ptr(val) }.to_str()?;
             map.insert(key.to_string(), val.to_string());
         } else {
+            // Internal Error -> panic
             panic!(\"odd number of items in hash table\");
         }
     }
-    map
+    Ok(map)
 }
 
-fn struct_list<T, S: convert::From<*const T>>(l: *const RawList<T>) -> Vec<S> {
+fn struct_list<T, S: TryFrom<*const T, Error = Error>>(
+    l: *const RawList<T>,
+) -> Result<Vec<S>, Error> {
     let mut v = Vec::new();
-    for x in unsafe {&*l}.iter() {
-        v.push(S::from(x));
+    for x in unsafe { &*l }.iter() {
+        v.push(S::try_from(x)?);
     }
-    v
+    Ok(v)
 }
 
-fn string_list (l: *const *const c_char) -> Vec<String> {
+fn string_list(l: *const *const c_char) -> Result<Vec<String>, Error> {
     let mut v = Vec::new();
     for x in NullTerminatedIter::new(l) {
-        let s = unsafe { ffi::CStr::from_ptr(x) }.to_str().unwrap();
+        let s = unsafe { ffi::CStr::from_ptr(x) }.to_str()?;
         v.push(s.to_string());
     }
-    v
-}
-
-fn free_string_list(l: *const *const c_char) {
-    // TODO
+    Ok(v)
 }
 
 #[derive(Debug)]
-pub struct Error {
+pub struct APIError {
     operation: &'static str,
     message: String,
-    errno: i32
+    errno: i32,
+}
+
+#[derive(Debug)]
+pub enum Error {
+    API(APIError),
+    IllegalString(ffi::NulError),
+    Utf8Error(str::Utf8Error),
+}
+
+impl convert::From<ffi::NulError> for Error {
+    fn from(error: ffi::NulError) -> Self {
+        Error::IllegalString(error)
+    }
+}
+
+impl convert::From<str::Utf8Error> for Error {
+    fn from(error: str::Utf8Error) -> Self {
+        Error::Utf8Error(error)
+    }
 }
 
 impl Handle {
@@ -271,13 +297,17 @@ impl Handle {
     fn get_error_from_handle(&self, operation: &'static str) -> Error {
         let c_msg = unsafe { guestfs_last_error(self.g) };
         let message = unsafe { ffi::CStr::from_ptr(c_msg).to_str().unwrap().to_string() };
-        let errno = unsafe { guestfs_last_errno(self.g) } ;
-        Error { operation, message, errno }
+        let errno = unsafe { guestfs_last_errno(self.g) };
+        Error::API(APIError {
+            operation,
+            message,
+            errno,
+        })
     }
 }
 
 pub struct UUID {
-    uuid: [u8; 32]
+    uuid: [u8; 32],
 }
 
 impl UUID {
@@ -288,7 +318,6 @@ impl UUID {
         self.uuid
     }
 }
-
 ";
   List.iter (
     fun { s_camel_name = name; s_name = c_name; s_cols = cols } ->
@@ -325,22 +354,24 @@ impl UUID {
       ) cols;
       pr "}\n";
       pr "\n";
-      pr "impl convert::From<*const Raw%s> for %s {\n" name name;
-      pr "    fn from(raw: *const Raw%s) -> Self {\n" name;
-      pr "        unsafe { %s {\n" name;
+      pr "impl TryFrom<*const Raw%s> for %s {\n" name name;
+      pr "    type Error = Error;\n";
+      pr "    fn try_from(raw: *const Raw%s) -> Result<Self, Self::Error> {\n" name;
+      pr "        Ok(unsafe {\n";
+      pr "            %s {\n" name;
       List.iter (
         fun x ->
-          indent 3;
+          indent 4;
           match x with
           | n, FChar ->
             pr "%s: (*raw).%s as i8,\n" n n;
           | n, FString ->
             pr "%s: {\n" n;
-            indent 4;
+            indent 5;
             pr "let s = ffi::CStr::from_ptr((*raw).%s);\n" n;
+            indent 5;
+            pr "s.to_str()?.to_string()\n";
             indent 4;
-            pr "s.to_str().unwrap().to_string()\n";
-            indent 3;
             pr "},\n"
           | n, FBuffer ->
             pr "%s: slice::from_raw_parts((*raw).%s as *const u8, (*raw).%s_len).to_vec(),\n" n n n
@@ -349,9 +380,14 @@ impl UUID {
           | n, (FUInt32 | FInt32 | FUInt64 | FInt64 | FBytes) ->
             pr "%s: (*raw).%s,\n" n n
           | n, FOptPercent ->
-            pr "%s: if (*raw).%s < 0.0 { None } else { Some((*raw).%s) },\n" n n n
+            pr "%s: if (*raw).%s < 0.0 {\n" n n;
+            indent 4; pr "    None\n";
+            indent 4; pr "} else {\n";
+            indent 4; pr "    Some((*raw).%s)\n" n;
+            indent 4; pr"},\n"
       ) cols;
-      pr "        } }\n";
+      pr "            }\n";
+      pr "        })\n";
       pr "    }\n";
       pr "}\n"
   ) external_structs;
@@ -361,11 +397,26 @@ impl UUID {
   pr "extern \"C\" {\n";
   List.iter (
     fun {  s_camel_name = name; s_name = c_name; } ->
-      pr "fn guestfs_free_%s(v: *const Raw%s);\n" c_name name;
-      pr "fn guestfs_free_%s_list(l: *const RawList<Raw%s>);\n" c_name name;
+      pr "    #[allow(dead_code)]\n";
+      pr "    fn guestfs_free_%s(v: *const Raw%s);\n" c_name name;
+      pr "    #[allow(dead_code)]\n";
+      pr "    fn guestfs_free_%s_list(l: *const RawList<Raw%s>);\n" c_name name;
   ) external_structs;
   pr "}\n";
 
+  (* [Outline] There are three types for each optional structs: SOptArgs,
+   * CExprSOptArgs, RawSOptArgs.
+   * SOptArgs: for Rust bindings' API. This can be seen by bindings' users.
+   * CExprSOptArgs: Each field has C expression(e.g. CString, *const c_char)
+   * RawSOptArgs: Each field has raw pointers or integer values
+   *
+   * SOptArgs ---try_into()---> CExprSOptArgs ---to_raw()---> RawSOptArgs
+   *
+   * Note: direct translation from SOptArgs to RawSOptArgs will cause a memory
+   * management problem. Using into_raw/from_raw, this problem can be avoided,
+   * but it is complex to handle freeing memories manually in Rust because of
+   * panic/?/etc.
+   *)
 
   (* generate structs for optional arguments *)
   List.iter (
@@ -383,27 +434,75 @@ impl UUID {
         pr "\n";
         pr "/* Optional Structs */\n";
         pr "#[derive(Default)]\n";
-        pr "pub struct OptArgs%s%s {\n" cname opt_life_parameter;
+        pr "pub struct %sOptArgs%s {\n" cname opt_life_parameter;
         List.iter (
           fun optarg ->
             let n = translate_bad_symbols (name_of_optargt optarg) in
             match optarg with
             | OBool _ ->
-              pr "    _%s: Option<bool>,\n" n
+              pr "    pub %s: Option<bool>,\n" n
             | OInt _ ->
-              pr "    _%s: Option<i32>,\n" n
+              pr "    pub %s: Option<i32>,\n" n
             | OInt64 _ ->
-              pr "    _%s: Option<i64>,\n" n
+              pr "    pub %s: Option<i64>,\n" n
             | OString _ ->
-              pr "    _%s: Option<&'a str>,\n" n
+              pr "    pub %s: Option<&'a str>,\n" n
             | OStringList _ ->
-              pr "    _%s: Option<Vec<&'a str>>,\n" n
+              pr "    pub %s: Option<&'a [&'a str]>,\n" n
         ) optargs;
         pr "}\n\n";
 
+        pr "struct CExpr%sOptArgs {\n" cname;
+        List.iter (
+          fun optarg ->
+            let n = translate_bad_symbols (name_of_optargt optarg) in
+            match optarg with
+            | OBool _ | OInt _ ->
+              pr "    %s: Option<c_int>,\n" n
+            | OInt64 _ ->
+              pr "    %s: Option<i64>,\n" n
+            | OString _ ->
+              pr "    %s: Option<ffi::CString>,\n" n
+            | OStringList _ ->
+              (* buffers and their pointer vector *)
+              pr "    %s: Option<(Vec<ffi::CString>, Vec<*const c_char>)>,\n" n
+        ) optargs;
+        pr "}\n\n";
+
+        pr "impl%s TryFrom<%sOptArgs%s> for CExpr%sOptArgs {\n"
+          opt_life_parameter cname opt_life_parameter cname;
+        pr "    type Error = Error;\n";
+        pr "    fn try_from(optargs: %sOptArgs%s) -> Result<Self, Self::Error> {\n" cname opt_life_parameter;
+        pr "        Ok(CExpr%sOptArgs {\n" cname;
+        List.iteri (
+          fun index optarg ->
+            let n = translate_bad_symbols (name_of_optargt optarg) in
+            match optarg with
+            | OBool _ ->
+              pr "        %s: optargs.%s.map(|b| if b { 1 } else { 0 }),\n" n n;
+            | OInt _ | OInt64 _  ->
+              pr "        %s: optargs.%s, \n" n n;
+            | OString _ ->
+              pr "        %s: optargs.%s.map(|v| ffi::CString::new(v)).transpose()?,\n" n n;
+            | OStringList _ ->
+              pr "        %s: optargs.%s.map(\n" n n;
+              pr "                |v| Ok::<_, Error>({\n";
+              pr "                         let v = arg_string_list(v)?;\n";
+              pr "                         let mut w = (&v).into_iter()\n";
+              pr "                                         .map(|v| v.as_ptr())\n";
+              pr "                                         .collect::<Vec<_>>();\n";
+              pr "                         w.push(ptr::null());\n";
+              pr "                         (v, w)\n";
+              pr "                    })\n";
+              pr "                ).transpose()?,\n";
+        ) optargs;
+        pr "         })\n";
+        pr "    }\n";
+        pr "}\n";
+
         (* raw struct for C bindings *)
         pr "#[repr(C)]\n";
-        pr "struct RawOptArgs%s {\n" cname;
+        pr "struct Raw%sOptArgs {\n" cname;
         pr "    bitmask: u64,\n";
         List.iter (
           fun optarg ->
@@ -422,41 +521,33 @@ impl UUID {
         ) optargs;
         pr "}\n\n";
 
-        pr "impl%s convert::From<OptArgs%s%s> for RawOptArgs%s {\n"
-          opt_life_parameter cname opt_life_parameter cname;
-        pr "    fn from(optargs: OptArgs%s%s) -> Self {\n" cname opt_life_parameter;
+        pr "impl convert::From<&CExpr%sOptArgs> for Raw%sOptArgs {\n"
+          cname cname;
+        pr "    fn from(optargs: &CExpr%sOptArgs) -> Self {\n" cname;
         pr "        let mut bitmask = 0;\n";
-        pr "         RawOptArgs%s {\n" cname;
+        pr "        Raw%sOptArgs {\n" cname;
         List.iteri (
           fun index optarg ->
             let n = translate_bad_symbols (name_of_optargt optarg) in
             match optarg with
-            | OBool _ ->
-              pr "        %s: if let Some(v) = optargs._%s {\n" n n;
-              pr "            bitmask |= 1 << %d;\n" index;
-              pr "            if v { 1 } else { 0 }\n";
-              pr "        } else {\n";
-              pr "            0\n";
-              pr "        },\n";
-            | OInt _ | OInt64 _  ->
-              pr "        %s: if let Some(v) = optargs._%s {\n" n n;
+            | OBool _ | OInt _ | OInt64 _  ->
+              pr "        %s: if let Some(v) = optargs.%s {\n" n n;
               pr "            bitmask |= 1 << %d;\n" index;
               pr "            v\n";
               pr "        } else {\n";
               pr "            0\n";
               pr "        },\n";
             | OString _ ->
-              pr "        %s: if let Some(v) = optargs._%s {\n" n n;
+              pr "        %s: if let Some(ref v) = optargs.%s {\n" n n;
               pr "            bitmask |= 1 << %d;\n" index;
-              pr "            let y: &str = &v;\n";
-              pr "            ffi::CString::new(y).unwrap().as_ptr()\n";
+              pr "            v.as_ptr()\n";
               pr "        } else {\n";
               pr "            ptr::null()\n";
               pr "        },\n";
             | OStringList _ ->
-              pr "        %s: if let Some(v) = optargs._%s {\n" n n;
+              pr "        %s: if let Some((_, ref v)) = optargs.%s {\n" n n;
               pr "            bitmask |= 1 << %d;\n" index;
-              pr "            arg_string_list(&v).as_ptr()";
+              pr "            v.as_ptr()\n";
               pr "        } else {\n";
               pr "            ptr::null()\n";
               pr "        },\n";
@@ -465,29 +556,6 @@ impl UUID {
         pr "         }\n";
         pr "    }\n";
         pr "}\n";
-
-        pr "impl%s OptArgs%s%s {\n" opt_life_parameter cname opt_life_parameter;
-        List.iter (
-          fun optarg ->
-            let n = translate_bad_symbols (name_of_optargt optarg) in
-            pr "    pub fn %s(self, %s: " n n;
-            (match optarg with
-            | OBool _ ->
-              pr "bool"
-            | OInt _ ->
-              pr "i32"
-            | OInt64 _ ->
-              pr "i64"
-            | OString _ ->
-              pr "&'a str"
-            | OStringList _ ->
-              pr "Vec<&'a str>"
-            );
-            pr ") -> OptArgs%s%s {\n" cname opt_life_parameter;
-            pr "        OptArgs%s { _%s: Some(%s), ..self }\n" cname n n;
-            pr "    }\n"
-        ) optargs;
-        pr "}\n\n";
       );
   ) (actions |> external_functions |> sort);
 
@@ -497,7 +565,8 @@ impl UUID {
     fun ({ name = name; shortdesc = shortdesc;
           style = (ret, args, optargs) } as f) ->
       let cname = snake2caml name in
-      pr "fn %s(g: *const guestfs_h" f.c_function;
+      pr "    #[allow(non_snake_case)]\n";
+      pr "    fn %s(g: *const guestfs_h" f.c_function;
       List.iter (
         fun arg ->
           pr ", ";
@@ -516,7 +585,7 @@ impl UUID {
        | _ -> ()
       );
       if optargs <> [] then
-        pr ", optarg: *const RawOptArgs%s" cname;
+        pr ", optarg: *const Raw%sOptArgs" cname;
 
       pr ") -> ";
 
@@ -541,14 +610,16 @@ impl UUID {
 
   pr "impl Handle {\n";
   List.iter (
-    fun ({ name = name; shortdesc = shortdesc;
+    fun ({ name = name; shortdesc = shortdesc; longdesc = longdesc;
           style = (ret, args, optargs) } as f) ->
       let cname = snake2caml name in
-      pr "    /// %s \n" shortdesc;
+      pr "    /// %s\n" shortdesc;
+      pr "    #[allow(non_snake_case)]\n";
       pr "    pub fn %s" name;
 
       (* generate arguments *)
       pr "(&self, ";
+
       let comma = ref false in
       List.iter (
         fun arg ->
@@ -560,14 +631,14 @@ impl UUID {
           | Int64 n -> pr "%s: i64" n
           | String (_, n) -> pr "%s: &str" n
           | OptString n -> pr "%s: Option<&str>" n
-          | StringList (_, n) -> pr "%s: Vec<&str>" n
-          | BufferIn n -> pr "%s: Vec<u8>" n
-          | Pointer (_, n) -> pr "%s: usize" n
+          | StringList (_, n) -> pr "%s: &[&str]" n
+          | BufferIn n -> pr "%s: &[u8]" n
+          | Pointer (_, n) -> pr "%s: *mut c_void" n
       ) args;
       if optargs <> [] then (
         if !comma then pr ", ";
         comma := true;
-        pr "optargs: OptArgs%s" cname
+        pr "optargs: %sOptArgs" cname
       );
       pr ")";
 
@@ -592,45 +663,37 @@ impl UUID {
       | RBufferOut _ -> pr "Vec<u8>");
       pr ", Error> {\n";
 
+
       let _pr = pr in
       let pr fs = indent 2; pr fs in
       List.iter (
         function
         | Bool n ->
-          pr "let c_%s = if %s { 1 } else { 0 };\n" n n
+          pr "let %s = if %s { 1 } else { 0 };\n" n n
         | String (_, n) ->
-          (* TODO: handle errors *)
-          pr "let c_%s = \n" n;
-          pr "    ffi::CString::new(%s).expect(\"CString::new failed\").as_ptr();\n" n;
+          pr "let c_%s = ffi::CString::new(%s)?;\n" n n;
         | OptString n ->
-          pr "let c_%s = match %s {" n n;
-          pr "    Some(s) => \n";
-          pr "        ffi::CString::new(s)\n";
-          pr "            .expect(\"CString::new failed\")\n";
-          pr "            .as_ptr(),\n";
-          pr "    None => ptr::null(),\n";
-          pr "};\n";
+          pr "let c_%s = %s.map(|s| ffi::CString::new(s)).transpose()?;\n" n n;
         | StringList (_, n) ->
-          pr "let c_%s = arg_string_list(&%s).as_ptr();\n" n n;
+          pr "let c_%s_v = arg_string_list(%s)?;\n" n n;
+          pr "let mut c_%s = (&c_%s_v).into_iter().map(|v| v.as_ptr()).collect::<Vec<_>>();\n" n n;
+          pr "c_%s.push(ptr::null());\n" n;
         | BufferIn n ->
-          pr "let c_%s_len = (&%s).len();\n" n n;
-          pr "let c_%s = ffi::CString::new(%s)\n" n n;
-          pr "            .expect(\"CString::new failed\")\n";
-          pr "            .as_ptr();\n";
+          pr "let c_%s_len = %s.len();\n" n n;
+          pr "let c_%s = unsafe { ffi::CString::from_vec_unchecked(%s.to_vec())};\n" n n;
         | Int _ | Int64 _ | Pointer _ -> ()
       ) args;
 
-      (* TODO: handle optargs *)
-      if optargs <> [] then (
-        pr "let optargs_raw = RawOptArgs%s::from(optargs);\n" cname;
-      );
-
       (match ret with
        | RBufferOut _ ->
          pr "let mut size = 0usize;\n"
        | _ -> ()
       );
 
+      if optargs <> [] then (
+        pr "let optargs_cexpr = CExpr%sOptArgs::try_from(optargs)?;\n" cname;
+      );
+
       pr "\n";
 
       pr "let r = unsafe { %s(self.g" f.c_function;
@@ -639,18 +702,19 @@ impl UUID {
         fun arg ->
           pr ", ";
           match arg with
-          | Bool n | String (_, n) | OptString n -> pr "c_%s" n
-          | Int n | Int64 n -> pr "%s" n
-          | Pointer _ -> pr "ptr::null()" (* XXX: what is pointer? *)
-          | StringList (_, n) -> pr "c_%s as *const *const c_char" n
-          | BufferIn n -> pr "c_%s, c_%s_len" n n
+          | String (_, n)  -> pr "(&c_%s).as_ptr()" n
+          | OptString n -> pr "match &c_%s { Some(ref s) => s.as_ptr(), None => ptr::null() }\n" n
+          | StringList (_, n) -> pr "(&c_%s).as_ptr() as *const *const c_char" n
+          | Bool n | Int n | Int64 n | Pointer (_, n) -> pr "%s" n
+          | BufferIn n -> pr "(&c_%s).as_ptr(), c_%s_len" n n
       ) args;
       (match ret with
-       | RBufferOut _ -> pr ", &size as *const usize"
+       | RBufferOut _ -> pr ", &mut size as *mut usize"
        | _ -> ()
       );
       if optargs <> [] then (
-        pr ", &optargs_raw as *const RawOptArgs%s" cname;
+        pr ", &(Raw%sOptArgs::from(&optargs_cexpr)) as *const Raw%sOptArgs"
+          cname cname;
       );
       pr ") };\n";
 
@@ -660,66 +724,85 @@ impl UUID {
        | `CannotReturnError -> ()
        | `ErrorIsMinusOne ->
          pr "if r == -1 {\n";
-         pr "    return Err(self.get_error_from_handle (\"%s\"));\n" name;
+         pr "    return Err(self.get_error_from_handle(\"%s\"));\n" name;
          pr "}\n"
        | `ErrorIsNULL ->
          pr "if r.is_null() {\n";
-         pr "    return Err(self.get_error_from_handle (\"%s\"));\n" name;
+         pr "    return Err(self.get_error_from_handle(\"%s\"));\n" name;
          pr "}\n"
       );
+
+      (* This part is not required, but type system will guarantee that
+       * the buffers are still alive. This is useful because Rust cannot
+       * know whether raw pointers used above are alive or not.
+       *)
+      List.iter (
+        function
+        | Bool _ | Int _ | Int64 _ | Pointer _ -> ()
+        | String (_, n)
+        | OptString n
+        | BufferIn n -> pr "drop(c_%s);\n" n;
+        | StringList (_, n) ->
+          pr "drop(c_%s);\n" n;
+          pr "drop(c_%s_v);\n" n;
+      ) args;
+      if optargs <> [] then (
+        pr "drop(optargs_cexpr);\n";
+      );
+
       pr "Ok(";
       let pr = _pr in
+      let pr3 fs = indent 3; pr fs in
       (match ret with
        | RErr -> pr "()"
        | RInt _ | RInt64 _ -> pr "r"
        | RBool _ -> pr "r != 0"
        | RConstString _ ->
-         pr "unsafe{ ffi::CStr::from_ptr(r) }.to_str().unwrap()\n"
+         pr "unsafe{ ffi::CStr::from_ptr(r) }.to_str()?"
        | RString _ ->
-         pr "{";
-         pr "    let s = unsafe {ffi::CStr::from_ptr(r)}\n";
-         pr "       .to_str().unwrap().to_string();\n";
-         pr "    unsafe { free(r as * const c_void) };\n";
-         pr "    s\n";
-         pr "}\n";
+         pr "{\n";
+         pr3 "let s = unsafe { ffi::CStr::from_ptr(r) };\n";
+         pr3 "unsafe { free(r as *const c_void) };\n";
+         pr3 "s.to_str()?.to_string()\n";
+         indent 2; pr "}";
        | RConstOptString _ ->
-         indent 3; pr "if r.is_null() {\n";
-         indent 3; pr "    None\n";
-         indent 3; pr "} else {\n";
-         indent 3; pr "    Some(unsafe { ffi::CStr::from_ptr(r) }.to_str().unwrap())\n";
-         indent 3; pr "}";
+         pr "if r.is_null() {\n";
+         pr3 "None\n";
+         indent 2; pr "} else {\n";
+         pr3 "Some(unsafe { ffi::CStr::from_ptr(r) }.to_str()?)\n";
+         indent 2; pr "}";
        | RStringList _ ->
          pr "{\n";
-         pr "    let s = string_list(r);\n";
-         pr "    free_string_list(r);\n";
-         pr "    s\n";
-         pr "}\n";
+         pr3 "let s = string_list(r);\n";
+         pr3 "free_string_list(r);\n";
+         pr3 "s?\n";
+         indent 2; pr "}";
        | RStruct (_, n) ->
          let sn = camel_name_of_struct n in
          pr "{\n";
-         pr "    let s = %s::from(r);\n" sn;
-         pr "    unsafe { guestfs_free_%s(r) };\n" n;
-         pr "    s\n";
-         pr "}\n";
+         pr3 "let s = %s::try_from(r);\n" sn;
+         pr3 "unsafe { guestfs_free_%s(r) };\n" n;
+         pr3 "s?\n";
+         indent 2; pr "}";
        | RStructList (_, n) ->
          let sn = camel_name_of_struct n in
          pr "{\n";
-         pr "    let l = struct_list::<Raw%s, %s>(r);\n" sn sn;
-         pr "    unsafe { guestfs_free_%s_list(r) };" n;
-         pr "    l\n";
-         pr "}\n";
+         pr3 "let l = struct_list::<Raw%s, %s>(r);\n" sn sn;
+         pr3 "unsafe { guestfs_free_%s_list(r) };\n" n;
+         pr3 "l?\n";
+         indent 2; pr "}";
        | RHashtable _ ->
          pr "{\n";
-         pr "    let h = hashmap(r);\n";
-         pr "    free_string_list(r);\n";
-         pr "    h\n";
-         pr "}\n";
+         pr3 "let h = hashmap(r);\n";
+         pr3 "free_string_list(r);\n";
+         pr3 "h?\n";
+         indent 2; pr "}";
        | RBufferOut _ ->
          pr "{\n";
-         pr "    let s = unsafe { slice::from_raw_parts(r, size) }.to_vec();\n";
-         pr "    unsafe { free(r as * const c_void) } ;\n";
-         pr "    s\n";
-         pr "}\n";
+         pr3 "let s = unsafe { slice::from_raw_parts(r, size) }.to_vec();\n";
+         pr3 "unsafe { free(r as *const c_void) } ;\n";
+         pr3 "s\n";
+         indent 2; pr "}";
       );
       pr ")\n";
       pr "    }\n\n"
diff --git a/generator/rust.mli b/generator/rust.mli
index 4fef55d4e..5410286c8 100644
--- a/generator/rust.mli
+++ b/generator/rust.mli
@@ -16,4 +16,7 @@
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 *)
 
-val generate_rust: unit -> unit
\ No newline at end of file
+val generate_rust: unit -> unit
+
+(* for bindtests.ml *)
+val snake2caml: string -> string
diff --git a/rust/.gitignore b/rust/.gitignore
new file mode 100644
index 000000000..53eaa2196
--- /dev/null
+++ b/rust/.gitignore
@@ -0,0 +1,2 @@
+/target
+**/*.rs.bk
diff --git a/rust/Makefile.am b/rust/Makefile.am
index e8bf27894..261cf4a5c 100644
--- a/rust/Makefile.am
+++ b/rust/Makefile.am
@@ -18,12 +18,26 @@
 include $(top_srcdir)/subdir-rules.mk
 
 generator_built = \
+	src/bin/bindtests.rs \
 	src/lib.rs
 
 EXTRA_DIST = \
-	$(generator_built)
+	.gitignore \
+	$(generator_built) \
+	tests/*.rs \
+	Cargo.toml \
+	Cargo.lock \
+	run-bindtests \
+	run-tests
 
 if HAVE_RUST
 
+all: src/lib.rs
+	$(top_builddir)/run $(CARGO) build --release
+
+TESTS = run-bindtests run-tests
+
+CLEANFILES += target/*~
+
 endif
 
diff --git a/rust/run-bindtests b/rust/run-bindtests
new file mode 100755
index 000000000..2986e898d
--- /dev/null
+++ b/rust/run-bindtests
@@ -0,0 +1,23 @@
+#!/bin/sh -
+# libguestfs Golang bindings
+# Copyright (C) 2013 Red Hat Inc.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+set -e
+
+$CARGO run --bin bindtests > bindtests.tmp
+diff -u $srcdir/../bindtests bindtests.tmp
+rm bindtests.tmp
diff --git a/rust/run-tests b/rust/run-tests
new file mode 100755
index 000000000..694ecc9dd
--- /dev/null
+++ b/rust/run-tests
@@ -0,0 +1,21 @@
+#!/bin/sh -
+# libguestfs Golang tests
+# Copyright (C) 2013 Red Hat Inc.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+set -e
+
+$CARGO test
diff --git a/rust/src/bin/.gitkeep b/rust/src/bin/.gitkeep
new file mode 100644
index 000000000..e69de29bb
diff --git a/rust/tests/010_load.rs b/rust/tests/010_load.rs
index eadd78896..4cb43f2c1 100644
--- a/rust/tests/010_load.rs
+++ b/rust/tests/010_load.rs
@@ -1,20 +1,20 @@
-/* libguestfs Python bindings
- Copyright (C) 2009-2019 Red Hat Inc.
+/* libguestfs Rust bindings
+Copyright (C) 2009-2019 Red Hat Inc.
 
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
 
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- GNU General Public License for more details.
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
 
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- */
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
 
 extern crate guestfs;
 
diff --git a/rust/tests/020_create.rs b/rust/tests/020_create.rs
index 0b57b19d7..017dbbac0 100644
--- a/rust/tests/020_create.rs
+++ b/rust/tests/020_create.rs
@@ -1,20 +1,20 @@
-/* libguestfs Python bindings
- Copyright (C) 2009-2019 Red Hat Inc.
+/* libguestfs Rust bindings
+Copyright (C) 2009-2019 Red Hat Inc.
 
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
 
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- GNU General Public License for more details.
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
 
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- */
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
 
 extern crate guestfs;
 
diff --git a/rust/tests/030_create_flags.rs b/rust/tests/030_create_flags.rs
index 5de0589c1..df3190d4c 100644
--- a/rust/tests/030_create_flags.rs
+++ b/rust/tests/030_create_flags.rs
@@ -1,19 +1,19 @@
-/* libguestfs Python bindings
- Copyright (C) 2009-2019 Red Hat Inc.
+/* libguestfs Rust bindings
+Copyright (C) 2009-2019 Red Hat Inc.
 
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
 
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- GNU General Public License for more details.
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
 
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- */
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
 
 extern crate guestfs;
 
@@ -23,8 +23,7 @@ use guestfs::*;
 fn create_flags() {
     let _h = Handle::create_flags(CreateFlags::none()).expect("create_flags fail");
     // TODO: Add parse_environment to check the flag is created correctly
-    let flags = CreateFlags::new()
-        .create_no_environment(true);
+    let flags = CreateFlags::new().create_no_environment(true);
     let _h = Handle::create_flags(flags).expect("create_flags fail");
     // TODO: Add parse_environment to check the flag is created correctly
 }
diff --git a/rust/tests/040_create_multiple.rs b/rust/tests/040_create_multiple.rs
index ee481c278..372fad7ee 100644
--- a/rust/tests/040_create_multiple.rs
+++ b/rust/tests/040_create_multiple.rs
@@ -1,20 +1,20 @@
-/* libguestfs Python bindings
- Copyright (C) 2009-2019 Red Hat Inc.
+/* libguestfs Rust bindings
+Copyright (C) 2009-2019 Red Hat Inc.
 
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
 
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- GNU General Public License for more details.
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
 
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- */
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
 
 extern crate guestfs;
 
diff --git a/rust/tests/050_handle_properties.rs b/rust/tests/050_handle_properties.rs
index 3c7b449f9..0b955d5cf 100644
--- a/rust/tests/050_handle_properties.rs
+++ b/rust/tests/050_handle_properties.rs
@@ -1,20 +1,20 @@
-/* libguestfs Python bindings
- Copyright (C) 2009-2019 Red Hat Inc.
+/* libguestfs Rust bindings
+Copyright (C) 2009-2019 Red Hat Inc.
 
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
 
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- GNU General Public License for more details.
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
 
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- */
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
 
 extern crate guestfs;
 
@@ -57,5 +57,6 @@ fn path() {
 #[test]
 fn add_drive() {
     let g = guestfs::Handle::create().expect("create");
-    g.add_drive("/dev/null", Default::default()).expect("add_drive");
+    g.add_drive("/dev/null", Default::default())
+        .expect("add_drive");
 }
diff --git a/rust/tests/070_opt_args.rs b/rust/tests/070_opt_args.rs
index 241c95b35..04b4890c2 100644
--- a/rust/tests/070_opt_args.rs
+++ b/rust/tests/070_opt_args.rs
@@ -1,20 +1,20 @@
-/* libguestfs Python bindings
- Copyright (C) 2009-2019 Red Hat Inc.
+/* libguestfs Rust bindings
+Copyright (C) 2009-2019 Red Hat Inc.
 
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
 
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- GNU General Public License for more details.
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
 
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- */
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
 
 extern crate guestfs;
 
@@ -30,7 +30,12 @@ fn no_optargs() {
 #[test]
 fn one_optarg() {
     let g = guestfs::Handle::create().expect("create");
-    g.add_drive("/dev/null",
-        guestfs::OptArgsAddDrive::default().readonly(true))
-        .expect("add_drive");
+    g.add_drive(
+        "/dev/null",
+        guestfs::AddDriveOptArgs {
+            readonly: Some(true),
+            ..Default::default()
+        },
+    )
+    .expect("add_drive");
 }
diff --git a/rust/tests/080_version.rs b/rust/tests/080_version.rs
index ab5314edc..19e441d67 100644
--- a/rust/tests/080_version.rs
+++ b/rust/tests/080_version.rs
@@ -1,29 +1,26 @@
-/* libguestfs Python bindings
- Copyright (C) 2009-2019 Red Hat Inc.
+/* libguestfs Rust bindings
+Copyright (C) 2009-2019 Red Hat Inc.
 
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
 
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- GNU General Public License for more details.
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
 
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- */
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
 
 extern crate guestfs;
 
-use std::default::Default;
-
 #[test]
 fn version() {
     let g = guestfs::Handle::create().expect("create");
     let v = g.version().expect("version");
     assert_eq!(v.major, 1)
 }
-
diff --git a/rust/tests/090_ret_values.rs b/rust/tests/090_ret_values.rs
new file mode 100644
index 000000000..d3e2e80da
--- /dev/null
+++ b/rust/tests/090_ret_values.rs
@@ -0,0 +1,61 @@
+/* libguestfs Rust bindings
+Copyright (C) 2009-2019 Red Hat Inc.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+extern crate guestfs;
+
+#[test]
+fn rint() {
+    let g = guestfs::Handle::create().expect("create");
+    assert_eq!(g.internal_test_rint("10").unwrap(), 10);
+    assert!(g.internal_test_rinterr().is_err())
+}
+
+#[test]
+fn rint64() {
+    let g = guestfs::Handle::create().expect("create");
+    assert_eq!(g.internal_test_rint64("10").unwrap(), 10);
+    assert!(g.internal_test_rint64err().is_err())
+}
+
+#[test]
+fn rbool() {
+    let g = guestfs::Handle::create().expect("create");
+    assert!(g.internal_test_rbool("true").unwrap());
+    assert!(!g.internal_test_rbool("false").unwrap());
+    assert!(g.internal_test_rboolerr().is_err())
+}
+
+#[test]
+fn rconststring() {
+    let g = guestfs::Handle::create().expect("create");
+    assert_eq!(
+        g.internal_test_rconststring("test").unwrap(),
+        "static string"
+    );
+    assert!(g.internal_test_rconststringerr().is_err())
+}
+
+#[test]
+fn rconstoptstring() {
+    let g = guestfs::Handle::create().expect("create");
+    assert_eq!(
+        g.internal_test_rconstoptstring("test").unwrap(),
+        Some("static string")
+    );
+    assert_eq!(g.internal_test_rconstoptstringerr().unwrap(), None)
+}
diff --git a/rust/tests/100_launch.rs b/rust/tests/100_launch.rs
new file mode 100644
index 000000000..1c1d8146a
--- /dev/null
+++ b/rust/tests/100_launch.rs
@@ -0,0 +1,65 @@
+/* libguestfs Rust bindings
+Copyright (C) 2009-2019 Red Hat Inc.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+extern crate guestfs;
+
+use std::default::Default;
+
+#[test]
+fn launch() {
+    let g = guestfs::Handle::create().expect("create");
+    g.add_drive_scratch(500 * 1024 * 1024, Default::default())
+        .expect("add_drive_scratch");
+    g.launch().expect("launch");
+    g.pvcreate("/dev/sda").expect("pvcreate");
+    g.vgcreate("VG", &["/dev/sda"]).expect("vgcreate");
+    g.lvcreate("LV1", "VG", 200).expect("lvcreate");
+    g.lvcreate("LV2", "VG", 200).expect("lvcreate");
+
+    let lvs = g.lvs().expect("lvs");
+    assert_eq!(
+        lvs,
+        vec!["/dev/VG/LV1".to_string(), "/dev/VG/LV2".to_string()]
+    );
+
+    g.mkfs("ext2", "/dev/VG/LV1", Default::default())
+        .expect("mkfs");
+    g.mount("/dev/VG/LV1", "/").expect("mount");
+    g.mkdir("/p").expect("mkdir");
+    g.touch("/q").expect("touch");
+
+    let mut dirs = g.readdir("/").expect("readdir");
+
+    dirs.sort_by(|a, b| a.name.cmp(&b.name));
+
+    let mut v = Vec::new();
+    for x in &dirs {
+        v.push((x.name.as_str(), x.ftyp as u8));
+    }
+    assert_eq!(
+        v,
+        vec![
+            (".", b'd'),
+            ("..", b'd'),
+            ("lost+found", b'd'),
+            ("p", b'd'),
+            ("q", b'r')
+        ]
+    );
+    g.shutdown().expect("shutdown");
+}
-- 
2.20.1 (Apple Git-117)




More information about the Libguestfs mailing list