Hiroyuki Katsura
2019-Jul-17 09:49 UTC
[Libguestfs] [PATCH] Rust bindings: Add Rust bindings
From: Hiroyuki_Katsura <hiroyuki.katsura.0513@gmail.com>
Rust bindings: Add create / close functions
Rust bindings: Add 4 bindings tests
Rust bindings: Add generator of structs
Rust bindings: Add generator of structs for optional arguments
Rust bindings: Add generator of function signatures
Rust bindings: Complete actions
Rust bindings: Fix memory management
Rust bindings: Add bindtests
Rust bindings: Add additional 4 bindings tests
Rust bindings: Format test files
Rust bindings: Incorporate bindings to build system
---
Makefile.am | 3 +
configure.ac | 6 +
generator/Makefile.am | 3 +
generator/bindtests.ml | 66 +++
generator/bindtests.mli | 1 +
generator/main.ml | 5 +
generator/rust.ml | 806 ++++++++++++++++++++++++++++
generator/rust.mli | 22 +
m4/guestfs-rust.m4 | 33 ++
run.in | 9 +
rust/.gitignore | 3 +
rust/Cargo.toml.in | 6 +
rust/Makefile.am | 42 ++
rust/run-bindtests | 23 +
rust/run-tests | 21 +
rust/src/.gitkeep | 0
rust/src/bin/.gitkeep | 0
rust/tests/.gitkeep | 0
rust/tests/010_load.rs | 24 +
rust/tests/020_create.rs | 24 +
rust/tests/030_create_flags.rs | 29 +
rust/tests/040_create_multiple.rs | 38 ++
rust/tests/050_handle_properties.rs | 62 +++
rust/tests/070_opt_args.rs | 41 ++
rust/tests/080_version.rs | 26 +
rust/tests/090_ret_values.rs | 61 +++
rust/tests/100_launch.rs | 65 +++
27 files changed, 1419 insertions(+)
create mode 100644 generator/rust.ml
create mode 100644 generator/rust.mli
create mode 100644 m4/guestfs-rust.m4
create mode 100644 rust/.gitignore
create mode 100644 rust/Cargo.toml.in
create mode 100644 rust/Makefile.am
create mode 100755 rust/run-bindtests
create mode 100755 rust/run-tests
create mode 100644 rust/src/.gitkeep
create mode 100644 rust/src/bin/.gitkeep
create mode 100644 rust/tests/.gitkeep
create mode 100644 rust/tests/010_load.rs
create mode 100644 rust/tests/020_create.rs
create mode 100644 rust/tests/030_create_flags.rs
create mode 100644 rust/tests/040_create_multiple.rs
create mode 100644 rust/tests/050_handle_properties.rs
create mode 100644 rust/tests/070_opt_args.rs
create mode 100644 rust/tests/080_version.rs
create mode 100644 rust/tests/090_ret_values.rs
create mode 100644 rust/tests/100_launch.rs
diff --git a/Makefile.am b/Makefile.am
index b5f33f62b..71cee94f6 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -151,6 +151,9 @@ endif
if HAVE_GOLANG
SUBDIRS += golang golang/examples
endif
+if HAVE_RUST
+SUBDIRS += rust
+endif
# Unconditional because nothing is built yet.
SUBDIRS += csharp
diff --git a/configure.ac b/configure.ac
index 6b701bef2..dc4ead709 100644
--- a/configure.ac
+++ b/configure.ac
@@ -161,6 +161,8 @@ HEADING([Checking for Go])
m4_include([m4/guestfs-golang.m4])
HEADING([Checking for GObject Introspection])
m4_include([m4/guestfs-gobject.m4])
+HEADING([Checking for Rust])
+m4_include([m4/guestfs-rust.m4])
HEADING([Checking for Vala])
VAPIGEN_CHECK
@@ -315,6 +317,8 @@ AC_CONFIG_FILES([Makefile
ruby/Rakefile
ruby/examples/Makefile
ruby/ext/guestfs/extconf.rb
+ rust/Makefile
+ rust/Cargo.toml
sparsify/Makefile
sysprep/Makefile
test-data/Makefile
@@ -428,6 +432,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/Makefile.am b/generator/Makefile.am
index 07872d49c..676c08ce5 100644
--- a/generator/Makefile.am
+++ b/generator/Makefile.am
@@ -105,6 +105,8 @@ sources = \
p2v_config.mli \
ruby.ml \
ruby.mli \
+ rust.ml \
+ rust.mli \
structs.ml \
structs.mli \
tests_c_api.ml \
@@ -163,6 +165,7 @@ objects = \
lua.cmo \
GObject.cmo \
golang.cmo \
+ rust.cmo \
bindtests.cmo \
errnostring.cmo \
customize.cmo \
diff --git a/generator/bindtests.ml b/generator/bindtests.ml
index 58d7897b3..e88e71c8a 100644
--- a/generator/bindtests.ml
+++ b/generator/bindtests.ml
@@ -983,6 +983,72 @@ and generate_php_bindtests ()
dump "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/bindtests.mli b/generator/bindtests.mli
index 6f469b3a1..0e18a4c44 100644
--- a/generator/bindtests.mli
+++ b/generator/bindtests.mli
@@ -28,3 +28,4 @@ val generate_perl_bindtests : unit -> unit
val generate_php_bindtests : unit -> unit
val generate_python_bindtests : unit -> unit
val generate_ruby_bindtests : unit -> unit
+val generate_rust_bindtests : unit -> unit
diff --git a/generator/main.ml b/generator/main.ml
index 7974550c5..ffe1bb95c 100644
--- a/generator/main.ml
+++ b/generator/main.ml
@@ -372,6 +372,11 @@ Run it from the top source directory using the command
output_to "p2v/virt-p2v-kernel-config.pod"
P2v_config.generate_p2v_kernel_config_pod;
+ output_to "rust/src/lib.rs"
+ Rust.generate_rust;
+ output_to "rust/src/bin/bindtests.rs"
+ Bindtests.generate_rust_bindtests;
+
(* Generate the list of files generated -- last. *)
printf "generated %d lines of code\n" (get_lines_generated ());
let files = List.sort compare (get_files_generated ()) in
diff --git a/generator/rust.ml b/generator/rust.ml
new file mode 100644
index 000000000..b7bc76da8
--- /dev/null
+++ b/generator/rust.ml
@@ -0,0 +1,806 @@
+(* libguestfs
+ * Copyright (C) 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
+*)
+
+(* Please read generator/README first. *)
+
+open Std_utils
+open Types
+open Utils
+open Pr
+open Docstrings
+open Optgroups
+open Actions
+open Structs
+open C
+open Events
+
+(* Utilities for Rust *)
+(* Are there corresponding functions to them? *)
+(* Should they be placed in utils.ml? *)
+let rec indent n = match n with
+ | x when x > 0 -> pr " "; indent (x - 1)
+ | _ -> ()
+
+(* split_on_char exists since OCaml 4.04 *)
+(* but current requirements: >=4.01 *)
+let split_on_char c = Str.split (Str.regexp (String.make 1 c))
+
+let snake2caml name + let l = split_on_char '_' name in
+ let l = List.map (fun x -> String.capitalize_ascii x) l in
+ String.concat "" l
+
+(* because there is a function which contains 'unsafe' field *)
+let black_list = ["unsafe"]
+
+let translate_bad_symbols s + if List.exists (fun x -> s = x) black_list
then
+ s ^ "_"
+ else
+ s
+
+let generate_rust () + generate_header CStyle LGPLv2plus;
+
+ pr "
+use std::collections;
+use std::convert;
+use std::convert::TryFrom;
+use std::ffi;
+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
+
+#[link(name = \"guestfs\")]
+extern \"C\" {
+ fn guestfs_create() -> *mut guestfs_h;
+ fn guestfs_create_flags(flags: i64) -> *mut guestfs_h;
+ fn guestfs_close(g: *mut guestfs_h);
+ fn guestfs_last_error(g: *mut guestfs_h) -> *const c_char;
+ fn guestfs_last_errno(g: *mut guestfs_h) -> c_int;
+}
+
+extern \"C\" {
+ fn free(buf: *const c_void);
+}
+
+const GUESTFS_CREATE_NO_ENVIRONMENT: i64 = 1;
+const GUESTFS_CREATE_NO_CLOSE_ON_EXIT: i64 = 2;
+
+pub struct Handle {
+ g: *mut guestfs_h,
+}
+
+impl Drop for Handle {
+ fn drop(&mut self) {
+ unsafe { guestfs_close(self.g) }
+ }
+}
+
+pub struct CreateFlags {
+ create_no_environment_flag: bool,
+ create_no_close_on_exit_flag: bool,
+}
+
+impl CreateFlags {
+ pub fn none() -> CreateFlags {
+ CreateFlags {
+ create_no_environment_flag: false,
+ create_no_close_on_exit_flag: false,
+ }
+ }
+
+ pub fn new() -> CreateFlags {
+ CreateFlags::none()
+ }
+
+ pub fn create_no_environment(mut self, flag: bool) -> CreateFlags {
+ self.create_no_environment_flag = flag;
+ self
+ }
+
+ pub fn create_no_close_on_exit_flag(mut self, flag: bool) -> CreateFlags
{
+ self.create_no_close_on_exit_flag = flag;
+ self
+ }
+
+ unsafe fn to_libc_int(self) -> i64 {
+ let mut flag = 0;
+ flag |= if self.create_no_environment_flag {
+ GUESTFS_CREATE_NO_ENVIRONMENT
+ } else {
+ 0
+ };
+ flag |= if self.create_no_close_on_exit_flag {
+ GUESTFS_CREATE_NO_CLOSE_ON_EXIT
+ } else {
+ 0
+ };
+ flag
+ }
+}
+
+struct NullTerminatedIter<T: Copy + Clone> {
+ p: *const *const T,
+}
+
+impl<T: Copy + Clone> NullTerminatedIter<T> {
+ fn new(p: *const *const T) -> NullTerminatedIter<T> {
+ NullTerminatedIter { p }
+ }
+}
+
+impl<T: Copy + Clone> Iterator for NullTerminatedIter<T> {
+ type Item = *const T;
+ fn next(&mut self) -> Option<*const T> {
+ let r = unsafe { *(self.p) };
+ if r.is_null() {
+ None
+ } else {
+ self.p = unsafe { self.p.offset(1) };
+ Some(r)
+ }
+ }
+}
+
+#[repr(C)]
+struct RawList<T> {
+ size: u32,
+ ptr: *const T,
+}
+
+struct RawListIter<'a, T> {
+ current: u32,
+ list: &'a RawList<T>,
+}
+
+impl<T> RawList<T> {
+ fn iter<'a>(&'a self) -> RawListIter<'a, T>
{
+ RawListIter {
+ current: 0,
+ list: self,
+ }
+ }
+}
+
+impl<'a, T> Iterator for RawListIter<'a, T> {
+ type Item = *const T;
+ fn next(&mut self) -> Option<*const T> {
+ if self.current >= self.list.size {
+ None
+ } else {
+ let elem = unsafe { self.list.ptr.offset(self.current as isize) };
+ self.current += 1;
+ Some(elem)
+ }
+ }
+}
+
+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;
+ 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) };
+ }
+ unsafe { free(l as *const c_void) };
+}
+
+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()?;
+ 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\");
+ }
+ }
+ Ok(map)
+}
+
+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::try_from(x)?);
+ }
+ Ok(v)
+}
+
+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()?;
+ v.push(s.to_string());
+ }
+ Ok(v)
+}
+
+#[derive(Debug)]
+pub struct APIError {
+ operation: &'static str,
+ message: String,
+ 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 {
+ pub fn create() -> Result<Handle, &'static str> {
+ let g = unsafe { guestfs_create() };
+ if g.is_null() {
+ Err(\"failed to create guestfs handle\")
+ } else {
+ Ok(Handle { g })
+ }
+ }
+
+ pub fn create_flags(flags: CreateFlags) -> Result<Handle,
&'static str> {
+ let g = unsafe { guestfs_create_flags(flags.to_libc_int()) };
+ if g.is_null() {
+ Err(\"failed to create guestfs handle\")
+ } else {
+ Ok(Handle { g })
+ }
+ }
+
+ 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::API(APIError {
+ operation,
+ message,
+ errno,
+ })
+ }
+}
+
+pub struct UUID {
+ uuid: [u8; 32],
+}
+
+impl UUID {
+ fn new(uuid: [u8; 32]) -> UUID {
+ UUID { uuid }
+ }
+ pub fn to_bytes(self) -> [u8; 32] {
+ self.uuid
+ }
+}
+";
+ List.iter (
+ fun { s_camel_name = name; s_name = c_name; s_cols = cols } ->
+ pr "\n";
+ pr "pub struct %s {\n" name;
+ List.iter (
+ function
+ | n, FChar -> pr " pub %s: i8,\n" n
+ | n, FString -> pr " pub %s: String,\n" n
+ | n, FBuffer -> pr " pub %s: Vec<u8>,\n" n
+ | n, FUInt32 -> pr " pub %s: u32,\n" n
+ | n, FInt32 -> pr " pub %s: i32,\n" n
+ | n, (FUInt64 | FBytes) -> pr " pub %s: u64,\n" n
+ | n, FInt64 -> pr " pub %s: i64,\n" n
+ | n, FUUID -> pr " pub %s: UUID,\n" n
+ | n, FOptPercent -> pr " pub %s: Option<f32>,\n"
n
+ ) cols;
+ pr "}\n";
+ pr "#[repr(C)]\n";
+ pr "struct Raw%s {\n" name;
+ List.iter (
+ function
+ | n, FChar -> pr " %s: c_char,\n" n
+ | n, FString -> pr " %s: *const c_char,\n" n
+ | n, FBuffer ->
+ pr " %s_len: usize,\n" n;
+ pr " %s: *const c_char,\n" n;
+ | n, FUUID -> pr " %s: [u8; 32],\n" n
+ | n, FUInt32 -> pr " %s: u32,\n" n
+ | n, FInt32 -> pr " %s: i32,\n" n
+ | n, (FUInt64 | FBytes) -> pr " %s: u64,\n" n
+ | n, FInt64 -> pr " %s: i64,\n" n
+ | n, FOptPercent -> pr " %s: f32,\n" n
+ ) cols;
+ pr "}\n";
+ pr "\n";
+ 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 4;
+ match x with
+ | n, FChar ->
+ pr "%s: (*raw).%s as i8,\n" n n;
+ | n, FString ->
+ pr "%s: {\n" n;
+ 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 "},\n"
+ | n, FBuffer ->
+ pr "%s: slice::from_raw_parts((*raw).%s as *const u8,
(*raw).%s_len).to_vec(),\n" n n n
+ | n, FUUID ->
+ pr "%s: UUID::new((*raw).%s),\n" n n
+ | n, (FUInt32 | FInt32 | FUInt64 | FInt64 | FBytes) ->
+ pr "%s: (*raw).%s,\n" n n
+ | n, FOptPercent ->
+ 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"
+ ) external_structs;
+
+ (* generate free functionf of structs *)
+ pr "\n";
+ pr "extern \"C\" {\n";
+ List.iter (
+ fun { s_camel_name = name; s_name = c_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 (
+ fun ({ name = name; shortdesc = shortdesc;
+ style = (ret, args, optargs) }) ->
+ let cname = snake2caml name in
+ let rec contains_ptr args = match args with
+ | [] -> false
+ | OString _ ::_
+ | OStringList _::_ -> true
+ | _::xs -> contains_ptr xs
+ in
+ let opt_life_parameter = if contains_ptr optargs then
"<'a>" else "" in
+ if optargs <> [] then (
+ pr "\n";
+ pr "/* Optional Structs */\n";
+ pr "#[derive(Default)]\n";
+ 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 " pub %s: Option<bool>,\n" n
+ | OInt _ ->
+ pr " pub %s: Option<i32>,\n" n
+ | OInt64 _ ->
+ pr " pub %s: Option<i64>,\n" n
+ | OString _ ->
+ pr " pub %s: Option<&'a str>,\n" n
+ | OStringList _ ->
+ 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 Raw%sOptArgs {\n" cname;
+ pr " bitmask: u64,\n";
+ List.iter (
+ fun optarg ->
+ let n = translate_bad_symbols (name_of_optargt optarg) in
+ match optarg with
+ | OBool _ ->
+ pr " %s: c_int,\n" n
+ | OInt _ ->
+ pr " %s: c_int,\n" n
+ | OInt64 _ ->
+ pr " %s: i64,\n" n
+ | OString _ ->
+ pr " %s: *const c_char,\n" n
+ | OStringList _ ->
+ pr " %s: *const *const c_char,\n" n
+ ) optargs;
+ pr "}\n\n";
+
+ 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 " Raw%sOptArgs {\n" cname;
+ List.iteri (
+ fun index optarg ->
+ let n = translate_bad_symbols (name_of_optargt optarg) in
+ match optarg with
+ | 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(ref v) = optargs.%s {\n" n
n;
+ pr " bitmask |= 1 << %d;\n" index;
+ pr " v.as_ptr()\n";
+ pr " } else {\n";
+ pr " ptr::null()\n";
+ pr " },\n";
+ | OStringList _ ->
+ pr " %s: if let Some((_, ref v)) = optargs.%s
{\n" n n;
+ pr " bitmask |= 1 << %d;\n" index;
+ pr " v.as_ptr()\n";
+ pr " } else {\n";
+ pr " ptr::null()\n";
+ pr " },\n";
+ ) optargs;
+ pr " bitmask,\n";
+ pr " }\n";
+ pr " }\n";
+ pr "}\n";
+ );
+ ) (actions |> external_functions |> sort);
+
+ (* extern C APIs *)
+ pr "extern \"C\" {\n";
+ List.iter (
+ fun ({ name = name; shortdesc = shortdesc;
+ style = (ret, args, optargs) } as f) ->
+ let cname = snake2caml name in
+ pr " #[allow(non_snake_case)]\n";
+ pr " fn %s(g: *const guestfs_h" f.c_function;
+ List.iter (
+ fun arg ->
+ pr ", ";
+ match arg with
+ | Bool n -> pr "%s: c_int" n
+ | String (_, n) -> pr "%s: *const c_char" n
+ | OptString n -> pr "%s: *const c_char" n
+ | Int n -> pr "%s: c_int" n
+ | Int64 n -> pr "%s: i64" n
+ | Pointer (_, n) -> pr "%s: *const ffi::c_void" n
+ | StringList (_, n) -> pr "%s: *const *const c_char" n
+ | BufferIn n -> pr "%s: *const c_char, %s_len: usize" n
n
+ ) args;
+ (match ret with
+ | RBufferOut _ -> pr ", size: *const usize"
+ | _ -> ()
+ );
+ if optargs <> [] then
+ pr ", optarg: *const Raw%sOptArgs" cname;
+
+ pr ") -> ";
+
+ (match ret with
+ | RErr | RInt _ | RBool _ -> pr "c_int"
+ | RInt64 _ -> pr "i64"
+ | RConstString _ | RString _ | RConstOptString _ -> pr "*const
c_char"
+ | RBufferOut _ -> pr "*const u8"
+ | RStringList _ | RHashtable _-> pr "*const *const c_char"
+ | RStruct (_, n) ->
+ let n = camel_name_of_struct n in
+ pr "*const Raw%s" n
+ | RStructList (_, n) ->
+ let n = camel_name_of_struct n in
+ pr "*const RawList<Raw%s>" n
+ );
+ pr ";\n";
+
+ ) (actions |> external_functions |> sort);
+ pr "}\n";
+
+
+ pr "impl Handle {\n";
+ List.iter (
+ fun ({ name = name; shortdesc = shortdesc; longdesc = longdesc;
+ style = (ret, args, optargs) } as f) ->
+ let cname = snake2caml name in
+ 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 ->
+ if !comma then pr ", ";
+ comma := true;
+ match arg with
+ | Bool n -> pr "%s: bool" n
+ | Int n -> pr "%s: i32" n
+ | Int64 n -> pr "%s: i64" n
+ | String (_, n) -> pr "%s: &str" n
+ | OptString n -> pr "%s: Option<&str>" 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: %sOptArgs" cname
+ );
+ pr ")";
+
+ (* generate return type *)
+ pr " -> Result<";
+ (match ret with
+ | RErr -> pr "()"
+ | RInt _ -> pr "i32"
+ | RInt64 _ -> pr "i64"
+ | RBool _ -> pr "bool"
+ | RConstString _ -> pr "&'static str"
+ | RString _ -> pr "String"
+ | RConstOptString _ -> pr "Option<&'static
str>"
+ | RStringList _ -> pr "Vec<String>"
+ | RStruct (_, sn) ->
+ let sn = camel_name_of_struct sn in
+ pr "%s" sn
+ | RStructList (_, sn) ->
+ let sn = camel_name_of_struct sn in
+ pr "Vec<%s>" sn
+ | RHashtable _ -> pr "collections::HashMap<String,
String>"
+ | 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 %s = if %s { 1 } else { 0 };\n" n n
+ | String (_, n) ->
+ pr "let c_%s = ffi::CString::new(%s)?;\n" n n;
+ | OptString n ->
+ pr "let c_%s = %s.map(|s|
ffi::CString::new(s)).transpose()?;\n" n n;
+ | StringList (_, 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 = unsafe {
ffi::CString::from_vec_unchecked(%s.to_vec())};\n" n n;
+ | Int _ | Int64 _ | Pointer _ -> ()
+ ) args;
+
+ (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;
+ let pr = _pr in
+ List.iter (
+ fun arg ->
+ pr ", ";
+ match arg with
+ | 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 ", &mut size as *mut usize"
+ | _ -> ()
+ );
+ if optargs <> [] then (
+ pr ", &(Raw%sOptArgs::from(&optargs_cexpr)) as *const
Raw%sOptArgs"
+ cname cname;
+ );
+ pr ") };\n";
+
+ let _pr = pr in
+ let pr fs = indent 2; pr fs in
+ (match errcode_of_ret ret with
+ | `CannotReturnError -> ()
+ | `ErrorIsMinusOne ->
+ pr "if r == -1 {\n";
+ 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 "}\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()?"
+ | RString _ ->
+ 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 _ ->
+ 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";
+ 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";
+ 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";
+ 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";
+ pr3 "let h = hashmap(r);\n";
+ pr3 "free_string_list(r);\n";
+ pr3 "h?\n";
+ indent 2; pr "}";
+ | RBufferOut _ ->
+ 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"
+ ) (actions |> external_functions |> sort);
+ pr "}\n"
diff --git a/generator/rust.mli b/generator/rust.mli
new file mode 100644
index 000000000..5410286c8
--- /dev/null
+++ b/generator/rust.mli
@@ -0,0 +1,22 @@
+(* libguestfs
+ * 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
+*)
+
+val generate_rust: unit -> unit
+
+(* for bindtests.ml *)
+val snake2caml: string -> string
diff --git a/m4/guestfs-rust.m4 b/m4/guestfs-rust.m4
new file mode 100644
index 000000000..48eee433e
--- /dev/null
+++ b/m4/guestfs-rust.m4
@@ -0,0 +1,33 @@
+# libguestfs
+# 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.
+
+dnl Rust
+AC_ARG_ENABLE([rust],
+ AS_HELP_STRING([--disable-rust], [disable Rust language bindings]),
+ [],
+ [enable_rust=yes])
+AS_IF([test "x$enable_rust" != "xno"],[
+ AC_CHECK_PROG([RUSTC],[rustc],[rustc],[no])
+ AC_CHECK_PROG([CARGO],[cargo],[cargo],[no])
+
+ AS_IF([test "x$RUSTC" == "xno"], [AC_MSG_WARN([rustc
not found])])
+ AS_IF([test "x$CARGO" == "xno"], [AC_MSG_WARN([cargo
not found])])
+],[
+ RUSTC=no
+ CARGO=no
+ ])
+AM_CONDITIONAL([HAVE_RUST],[test "x$RUSTC" != "xno"
&& test "x$CARGO" != "xno"])
diff --git a/run.in b/run.in
index 488e1b937..301b02664 100755
--- a/run.in
+++ b/run.in
@@ -201,6 +201,15 @@ else
fi
export CGO_LDFLAGS
+# For rust
+export RUST="@RUST@"
+if [ -z "$RUSTFLAGS" ]; then
+ RUSTFLAGS="-C link-args=-L$b/lib/.libs"
+else
+ RUSTFLAGS="$RUSTFLAGS -C link-args=-L$b/lib/.libs"
+fi
+export RUSTFLAGS
+
# For GObject, Javascript and friends.
export GJS="@GJS@"
prepend GI_TYPELIB_PATH "$b/gobject"
diff --git a/rust/.gitignore b/rust/.gitignore
new file mode 100644
index 000000000..693699042
--- /dev/null
+++ b/rust/.gitignore
@@ -0,0 +1,3 @@
+/target
+**/*.rs.bk
+Cargo.lock
diff --git a/rust/Cargo.toml.in b/rust/Cargo.toml.in
new file mode 100644
index 000000000..e25dfe768
--- /dev/null
+++ b/rust/Cargo.toml.in
@@ -0,0 +1,6 @@
+[package]
+name = "guestfs"
+version = "@VERSION@"
+edition = "2018"
+
+[dependencies]
diff --git a/rust/Makefile.am b/rust/Makefile.am
new file mode 100644
index 000000000..ff284ec00
--- /dev/null
+++ b/rust/Makefile.am
@@ -0,0 +1,42 @@
+# libguestfs golang bindings
+# Copyright (C) 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.
+
+include $(top_srcdir)/subdir-rules.mk
+
+generator_built = \
+ src/bin/bindtests.rs \
+ src/lib.rs
+
+EXTRA_DIST = \
+ .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/.gitkeep b/rust/src/.gitkeep
new file mode 100644
index 000000000..e69de29bb
diff --git a/rust/src/bin/.gitkeep b/rust/src/bin/.gitkeep
new file mode 100644
index 000000000..e69de29bb
diff --git a/rust/tests/.gitkeep b/rust/tests/.gitkeep
new file mode 100644
index 000000000..e69de29bb
diff --git a/rust/tests/010_load.rs b/rust/tests/010_load.rs
new file mode 100644
index 000000000..4cb43f2c1
--- /dev/null
+++ b/rust/tests/010_load.rs
@@ -0,0 +1,24 @@
+/* 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 load() {
+ // nop
+}
diff --git a/rust/tests/020_create.rs b/rust/tests/020_create.rs
new file mode 100644
index 000000000..017dbbac0
--- /dev/null
+++ b/rust/tests/020_create.rs
@@ -0,0 +1,24 @@
+/* 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 create() {
+ assert!(!guestfs::Handle::create().is_err(), "create fail");
+}
diff --git a/rust/tests/030_create_flags.rs b/rust/tests/030_create_flags.rs
new file mode 100644
index 000000000..df3190d4c
--- /dev/null
+++ b/rust/tests/030_create_flags.rs
@@ -0,0 +1,29 @@
+/* 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 guestfs::*;
+
+#[test]
+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 _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
new file mode 100644
index 000000000..372fad7ee
--- /dev/null
+++ b/rust/tests/040_create_multiple.rs
@@ -0,0 +1,38 @@
+/* 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;
+
+fn create() -> guestfs::Handle {
+ match guestfs::Handle::create() {
+ Ok(g) => g,
+ Err(e) => panic!("fail: {}", e),
+ }
+}
+
+fn ignore(_x: guestfs::Handle, _y: guestfs::Handle, _z: guestfs::Handle) {
+ // drop
+}
+
+#[test]
+fn create_multiple() {
+ let x = create();
+ let y = create();
+ let z = create();
+ ignore(x, y, z)
+}
diff --git a/rust/tests/050_handle_properties.rs
b/rust/tests/050_handle_properties.rs
new file mode 100644
index 000000000..0b955d5cf
--- /dev/null
+++ b/rust/tests/050_handle_properties.rs
@@ -0,0 +1,62 @@
+/* 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 verbose() {
+ let g = guestfs::Handle::create().expect("create");
+ g.set_verbose(true).expect("set_verbose");
+ assert_eq!(g.get_verbose().expect("get_verbose"), true);
+ g.set_verbose(false).expect("set_verbose");
+ assert_eq!(g.get_verbose().expect("get_verbose"), false);
+}
+
+#[test]
+fn trace() {
+ let g = guestfs::Handle::create().expect("create");
+ g.set_trace(true).expect("set_trace");
+ assert_eq!(g.get_trace().expect("get_trace"), true);
+ g.set_trace(false).expect("set_trace");
+ assert_eq!(g.get_trace().expect("get_trace"), false);
+}
+
+#[test]
+fn autosync() {
+ let g = guestfs::Handle::create().expect("create");
+ g.set_autosync(true).expect("set_autosync");
+ assert_eq!(g.get_autosync().expect("get_autosync"), true);
+ g.set_autosync(false).expect("set_autosync");
+ assert_eq!(g.get_autosync().expect("get_autosync"), false);
+}
+
+#[test]
+fn path() {
+ let g = guestfs::Handle::create().expect("create");
+ g.set_path(Some(".")).expect("set_path");
+ assert_eq!(g.get_path().expect("get_path"), ".");
+}
+
+#[test]
+fn add_drive() {
+ let g = guestfs::Handle::create().expect("create");
+ 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
new file mode 100644
index 000000000..04b4890c2
--- /dev/null
+++ b/rust/tests/070_opt_args.rs
@@ -0,0 +1,41 @@
+/* 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 no_optargs() {
+ let g = guestfs::Handle::create().expect("create");
+ g.add_drive("/dev/null", Default::default())
+ .expect("add_drive");
+}
+
+#[test]
+fn one_optarg() {
+ let g = guestfs::Handle::create().expect("create");
+ 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
new file mode 100644
index 000000000..19e441d67
--- /dev/null
+++ b/rust/tests/080_version.rs
@@ -0,0 +1,26 @@
+/* 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 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)
Martin Kletzander
2019-Jul-17 13:40 UTC
Re: [Libguestfs] [PATCH] Rust bindings: Add Rust bindings
On Wed, Jul 17, 2019 at 06:49:39PM +0900, Hiroyuki Katsura wrote:>From: Hiroyuki_Katsura <hiroyuki.katsura.0513@gmail.com> > >Rust bindings: Add create / close functions > >Rust bindings: Add 4 bindings tests > >Rust bindings: Add generator of structs > >Rust bindings: Add generator of structs for optional arguments > >Rust bindings: Add generator of function signatures > >Rust bindings: Complete actions > >Rust bindings: Fix memory management > >Rust bindings: Add bindtests > >Rust bindings: Add additional 4 bindings tests > >Rust bindings: Format test files > >Rust bindings: Incorporate bindings to build system >--- > Makefile.am | 3 + > configure.ac | 6 + > generator/Makefile.am | 3 + > generator/bindtests.ml | 66 +++ > generator/bindtests.mli | 1 + > generator/main.ml | 5 + > generator/rust.ml | 806 ++++++++++++++++++++++++++++ > generator/rust.mli | 22 + > m4/guestfs-rust.m4 | 33 ++ > run.in | 9 + > rust/.gitignore | 3 + > rust/Cargo.toml.in | 6 + > rust/Makefile.am | 42 ++ > rust/run-bindtests | 23 + > rust/run-tests | 21 + > rust/src/.gitkeep | 0 > rust/src/bin/.gitkeep | 0 > rust/tests/.gitkeep | 0 > rust/tests/010_load.rs | 24 + > rust/tests/020_create.rs | 24 + > rust/tests/030_create_flags.rs | 29 + > rust/tests/040_create_multiple.rs | 38 ++ > rust/tests/050_handle_properties.rs | 62 +++ > rust/tests/070_opt_args.rs | 41 ++ > rust/tests/080_version.rs | 26 + > rust/tests/090_ret_values.rs | 61 +++ > rust/tests/100_launch.rs | 65 +++ > 27 files changed, 1419 insertions(+) > create mode 100644 generator/rust.ml > create mode 100644 generator/rust.mli > create mode 100644 m4/guestfs-rust.m4 > create mode 100644 rust/.gitignore > create mode 100644 rust/Cargo.toml.in > create mode 100644 rust/Makefile.am > create mode 100755 rust/run-bindtests > create mode 100755 rust/run-tests > create mode 100644 rust/src/.gitkeep > create mode 100644 rust/src/bin/.gitkeep > create mode 100644 rust/tests/.gitkeep > create mode 100644 rust/tests/010_load.rs > create mode 100644 rust/tests/020_create.rs > create mode 100644 rust/tests/030_create_flags.rs > create mode 100644 rust/tests/040_create_multiple.rs > create mode 100644 rust/tests/050_handle_properties.rs > create mode 100644 rust/tests/070_opt_args.rs > create mode 100644 rust/tests/080_version.rs > create mode 100644 rust/tests/090_ret_values.rs > create mode 100644 rust/tests/100_launch.rs > >diff --git a/generator/rust.ml b/generator/rust.ml >new file mode 100644 >index 000000000..b7bc76da8 >--- /dev/null >+++ b/generator/rust.ml >@@ -0,0 +1,806 @@ >+(* libguestfs >+ * Copyright (C) 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 >+*) >+ >+(* Please read generator/README first. *) >+ >+open Std_utils >+open Types >+open Utils >+open Pr >+open Docstrings >+open Optgroups >+open Actions >+open Structs >+open C >+open Events >+ >+(* Utilities for Rust *) >+(* Are there corresponding functions to them? *) >+(* Should they be placed in utils.ml? *) >+let rec indent n = match n with >+ | x when x > 0 -> pr " "; indent (x - 1) >+ | _ -> () >+ >+(* split_on_char exists since OCaml 4.04 *) >+(* but current requirements: >=4.01 *) >+let split_on_char c = Str.split (Str.regexp (String.make 1 c)) >+ >+let snake2caml name >+ let l = split_on_char '_' name in >+ let l = List.map (fun x -> String.capitalize_ascii x) l in >+ String.concat "" l >+ >+(* because there is a function which contains 'unsafe' field *) >+let black_list = ["unsafe"] >+ >+let translate_bad_symbols s >+ if List.exists (fun x -> s = x) black_list then >+ s ^ "_" >+ else >+ s >+ >+let generate_rust () >+ generate_header CStyle LGPLv2plus; >+ >+ pr "I started with this as well (for the libnbd bindings), but it was a PITA for me to modify parts of the generator. I then realized that I found out two things, each one should help you move part of this outside the generator: 1) you can have impls for the same struct in different files 2) you can scope the `pub`, for example `pub(crate)` would make the definition public but only for this particular crate. That way you can share things between modules without exposing it to consumers of this crate>+use std::collections; >+use std::convert; >+use std::convert::TryFrom; >+use std::ffi; >+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 >+You should not use empty enums for ffi opaque structs as they can be optimized out. Also #[repr(C)] it. You should rather have a struct with an empty member: struct guestfs_handle { _unused: [u32; 0], } I'll try to find the official info in rust docs, I just do not know on which docs page it was. /me goes looking Oh yeah, it was in the nomicon: https://doc.rust-lang.org/nomicon/ffi.html#representing-opaque-structs It will be even nicer when RFC 1861 gets stabilized, but that's long long in the future, I guess: https://github.com/rust-lang/rust/issues/43467 Otherwise it looks very similar to what I started, so that should be good (hopefully) =) I hope I'll get some more time to go over the whole posting here and try it out as well. [...]>diff --git a/rust/Makefile.am b/rust/Makefile.am >new file mode 100644 >index 000000000..ff284ec00 >--- /dev/null >+++ b/rust/Makefile.am >@@ -0,0 +1,42 @@ >+# libguestfs golang bindingsgolang? =)>+# Copyright (C) 2019 Red Hat Inc.I'll let Rich figure out what is supposed to be here :)>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.dtto and some other files as well [...]>diff --git a/rust/tests/010_load.rs b/rust/tests/010_load.rs >new file mode 100644 >index 000000000..4cb43f2c1 >--- /dev/null >+++ b/rust/tests/010_load.rs >@@ -0,0 +1,24 @@ >+/* 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 load() { >+ // nop >+}Is this just trying if the guestfs can be linked with?>diff --git a/rust/tests/020_create.rs b/rust/tests/020_create.rs >new file mode 100644 >index 000000000..017dbbac0 >--- /dev/null >+++ b/rust/tests/020_create.rs >@@ -0,0 +1,24 @@ >+/* 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 create() { >+ assert!(!guestfs::Handle::create().is_err(), "create fail");Isn't Result.is_ok() same as !Result.is_err()? Maybe even unwrap() would work here as it would actually show the error that was returned in the backtrace. [...]>diff --git a/rust/tests/050_handle_properties.rs b/rust/tests/050_handle_properties.rs >new file mode 100644 >index 000000000..0b955d5cf >--- /dev/null >+++ b/rust/tests/050_handle_properties.rs >@@ -0,0 +1,62 @@ >+/* 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.Oh, you're also using the older version of the license here (with the old "logistics" mentioned), but I guess that's fine. I, for one, would ban these, but it's a recommendation to put them in each file (although I think it's an outdated one, but you never know with lawyers), so... </rant> Anyway, it looks good, although I just skimmed it and I haven't tested it. Martin
Hiroyuki Katsura
2019-Jul-20 06:55 UTC
Re: [Libguestfs] [PATCH] Rust bindings: Add Rust bindings
> Is this just trying if the guestfs can be linked with?Yes. In OCaml bindings, there is the corresponding test( https://github.com/libguestfs/libguestfs/blob/master/ocaml/t/guestfs_010_load.ml). I just mimicked it. If it is not required, I will remove it. divided the generated files and handmade files in rust/src/ directory. I'll send this fixed patch to this mailing list. I'm not sure about the license problems. Can you teach me that? Regards, Hiroyuki 2019年7月17日(水) 22:40 Martin Kletzander <mkletzan@redhat.com>:> On Wed, Jul 17, 2019 at 06:49:39PM +0900, Hiroyuki Katsura wrote: > >From: Hiroyuki_Katsura <hiroyuki.katsura.0513@gmail.com> > > > >Rust bindings: Add create / close functions > > > >Rust bindings: Add 4 bindings tests > > > >Rust bindings: Add generator of structs > > > >Rust bindings: Add generator of structs for optional arguments > > > >Rust bindings: Add generator of function signatures > > > >Rust bindings: Complete actions > > > >Rust bindings: Fix memory management > > > >Rust bindings: Add bindtests > > > >Rust bindings: Add additional 4 bindings tests > > > >Rust bindings: Format test files > > > >Rust bindings: Incorporate bindings to build system > >--- > > Makefile.am | 3 + > > configure.ac | 6 + > > generator/Makefile.am | 3 + > > generator/bindtests.ml | 66 +++ > > generator/bindtests.mli | 1 + > > generator/main.ml | 5 + > > generator/rust.ml | 806 ++++++++++++++++++++++++++++ > > generator/rust.mli | 22 + > > m4/guestfs-rust.m4 | 33 ++ > > run.in | 9 + > > rust/.gitignore | 3 + > > rust/Cargo.toml.in | 6 + > > rust/Makefile.am | 42 ++ > > rust/run-bindtests | 23 + > > rust/run-tests | 21 + > > rust/src/.gitkeep | 0 > > rust/src/bin/.gitkeep | 0 > > rust/tests/.gitkeep | 0 > > rust/tests/010_load.rs | 24 + > > rust/tests/020_create.rs | 24 + > > rust/tests/030_create_flags.rs | 29 + > > rust/tests/040_create_multiple.rs | 38 ++ > > rust/tests/050_handle_properties.rs | 62 +++ > > rust/tests/070_opt_args.rs | 41 ++ > > rust/tests/080_version.rs | 26 + > > rust/tests/090_ret_values.rs | 61 +++ > > rust/tests/100_launch.rs | 65 +++ > > 27 files changed, 1419 insertions(+) > > create mode 100644 generator/rust.ml > > create mode 100644 generator/rust.mli > > create mode 100644 m4/guestfs-rust.m4 > > create mode 100644 rust/.gitignore > > create mode 100644 rust/Cargo.toml.in > > create mode 100644 rust/Makefile.am > > create mode 100755 rust/run-bindtests > > create mode 100755 rust/run-tests > > create mode 100644 rust/src/.gitkeep > > create mode 100644 rust/src/bin/.gitkeep > > create mode 100644 rust/tests/.gitkeep > > create mode 100644 rust/tests/010_load.rs > > create mode 100644 rust/tests/020_create.rs > > create mode 100644 rust/tests/030_create_flags.rs > > create mode 100644 rust/tests/040_create_multiple.rs > > create mode 100644 rust/tests/050_handle_properties.rs > > create mode 100644 rust/tests/070_opt_args.rs > > create mode 100644 rust/tests/080_version.rs > > create mode 100644 rust/tests/090_ret_values.rs > > create mode 100644 rust/tests/100_launch.rs > > > >diff --git a/generator/rust.ml b/generator/rust.ml > >new file mode 100644 > >index 000000000..b7bc76da8 > >--- /dev/null > >+++ b/generator/rust.ml > >@@ -0,0 +1,806 @@ > >+(* libguestfs > >+ * Copyright (C) 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 > >+*) > >+ > >+(* Please read generator/README first. *) > >+ > >+open Std_utils > >+open Types > >+open Utils > >+open Pr > >+open Docstrings > >+open Optgroups > >+open Actions > >+open Structs > >+open C > >+open Events > >+ > >+(* Utilities for Rust *) > >+(* Are there corresponding functions to them? *) > >+(* Should they be placed in utils.ml? *) > >+let rec indent n = match n with > >+ | x when x > 0 -> pr " "; indent (x - 1) > >+ | _ -> () > >+ > >+(* split_on_char exists since OCaml 4.04 *) > >+(* but current requirements: >=4.01 *) > >+let split_on_char c = Str.split (Str.regexp (String.make 1 c)) > >+ > >+let snake2caml name > >+ let l = split_on_char '_' name in > >+ let l = List.map (fun x -> String.capitalize_ascii x) l in > >+ String.concat "" l > >+ > >+(* because there is a function which contains 'unsafe' field *) > >+let black_list = ["unsafe"] > >+ > >+let translate_bad_symbols s > >+ if List.exists (fun x -> s = x) black_list then > >+ s ^ "_" > >+ else > >+ s > >+ > >+let generate_rust () > >+ generate_header CStyle LGPLv2plus; > >+ > >+ pr " > > I started with this as well (for the libnbd bindings), but it was a PITA > for me > to modify parts of the generator. I then realized that I found out two > things, > each one should help you move part of this outside the generator: > > 1) you can have impls for the same struct in different files > > 2) you can scope the `pub`, for example `pub(crate)` would make the > definition > public but only for this particular crate. That way you can share > things > between modules without exposing it to consumers of this crate > > >+use std::collections; > >+use std::convert; > >+use std::convert::TryFrom; > >+use std::ffi; > >+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 > >+ > > You should not use empty enums for ffi opaque structs as they can be > optimized > out. Also #[repr(C)] it. > > You should rather have a struct with an empty member: > > struct guestfs_handle { > _unused: [u32; 0], > } > > I'll try to find the official info in rust docs, I just do not know on > which > docs page it was. > > /me goes looking > > Oh yeah, it was in the nomicon: > > https://doc.rust-lang.org/nomicon/ffi.html#representing-opaque-structs > > It will be even nicer when RFC 1861 gets stabilized, but that's long long > in the > future, I guess: > > https://github.com/rust-lang/rust/issues/43467 > > Otherwise it looks very similar to what I started, so that should be good > (hopefully) =) > > I hope I'll get some more time to go over the whole posting here and try > it out > as well. > > [...] > > >diff --git a/rust/Makefile.am b/rust/Makefile.am > >new file mode 100644 > >index 000000000..ff284ec00 > >--- /dev/null > >+++ b/rust/Makefile.am > >@@ -0,0 +1,42 @@ > >+# libguestfs golang bindings > > golang? =) > > >+# Copyright (C) 2019 Red Hat Inc. > > I'll let Rich figure out what is supposed to be here :) > > >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. > > dtto > > and some other files as well > > [...] > > >diff --git a/rust/tests/010_load.rs b/rust/tests/010_load.rs > >new file mode 100644 > >index 000000000..4cb43f2c1 > >--- /dev/null > >+++ b/rust/tests/010_load.rs > >@@ -0,0 +1,24 @@ > >+/* 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 load() { > >+ // nop > >+} > > Is this just trying if the guestfs can be linked with? > > >diff --git a/rust/tests/020_create.rs b/rust/tests/020_create.rs > >new file mode 100644 > >index 000000000..017dbbac0 > >--- /dev/null > >+++ b/rust/tests/020_create.rs > >@@ -0,0 +1,24 @@ > >+/* 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 create() { > >+ assert!(!guestfs::Handle::create().is_err(), "create fail"); > > Isn't Result.is_ok() same as !Result.is_err()? Maybe even unwrap() would > work > here as it would actually show the error that was returned in the > backtrace. > > [...] > > >diff --git a/rust/tests/050_handle_properties.rs b/rust/tests/ > 050_handle_properties.rs > >new file mode 100644 > >index 000000000..0b955d5cf > >--- /dev/null > >+++ b/rust/tests/050_handle_properties.rs > >@@ -0,0 +1,62 @@ > >+/* 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. > > Oh, you're also using the older version of the license here (with the old > "logistics" mentioned), but I guess that's fine. I, for one, would ban > these, > but it's a recommendation to put them in each file (although I think it's > an > outdated one, but you never know with lawyers), so... > </rant> > > Anyway, it looks good, although I just skimmed it and I haven't tested it. > > Martin >