I fixed the patch I submitted before based on comments, and there are some commits which are merged or divided. So, I will re-send all the patches. Regards, Hiroyuki Katsura
Hiroyuki Katsura
2019-Jul-02 13:14 UTC
[Libguestfs] [PATCH 01/12] Rust bindings: Add Rust bindings
From: Hiroyuki_Katsura <hiroyuki.katsura.0513@gmail.com> --- Makefile.am | 4 ++++ configure.ac | 3 +++ generator/Makefile.am | 3 +++ generator/bindtests.ml | 3 +++ generator/bindtests.mli | 1 + generator/main.ml | 5 +++++ generator/rust.ml | 34 ++++++++++++++++++++++++++++++++++ generator/rust.mli | 19 +++++++++++++++++++ m4/guestfs-rust.m4 | 30 ++++++++++++++++++++++++++++++ rust/Cargo.toml | 6 ++++++ rust/Makefile.am | 29 +++++++++++++++++++++++++++++ rust/src/.gitkeep | 0 rust/tests/.gitkeep | 0 13 files changed, 137 insertions(+) create mode 100644 generator/rust.ml create mode 100644 generator/rust.mli create mode 100644 m4/guestfs-rust.m4 create mode 100644 rust/Cargo.toml create mode 100644 rust/Makefile.am create mode 100644 rust/src/.gitkeep create mode 100644 rust/tests/.gitkeep diff --git a/Makefile.am b/Makefile.am index b5f33f62b..69142cf41 100644 --- a/Makefile.am +++ b/Makefile.am @@ -151,6 +151,10 @@ 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..f9bdbe54b 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,7 @@ AC_CONFIG_FILES([Makefile ruby/Rakefile ruby/examples/Makefile ruby/ext/guestfs/extconf.rb + rust/Makefile sparsify/Makefile sysprep/Makefile test-data/Makefile 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..8a5682d5e 100644 --- a/generator/bindtests.ml +++ b/generator/bindtests.ml @@ -983,6 +983,9 @@ and generate_php_bindtests () dump "bindtests" +and generate_rust_bindtests () + generate_header CStyle GPLv2plus + (* 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..5de89138c 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/tests/bind_test.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..40ca8d198 --- /dev/null +++ b/generator/rust.ml @@ -0,0 +1,34 @@ +(* 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 + +let generate_rust () + generate_header CStyle LGPLv2plus + diff --git a/generator/rust.mli b/generator/rust.mli new file mode 100644 index 000000000..4fef55d4e --- /dev/null +++ b/generator/rust.mli @@ -0,0 +1,19 @@ +(* 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 \ No newline at end of file diff --git a/m4/guestfs-rust.m4 b/m4/guestfs-rust.m4 new file mode 100644 index 000000000..f46ce960b --- /dev/null +++ b/m4/guestfs-rust.m4 @@ -0,0 +1,30 @@ +# 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]) +],[ + RUSTC=no + CARGO=no + ]) +AM_CONDITIONAL([HAVE_RUST],[test "x$RUSTC" != "xno" && test "x$CARGO" != "xno"]) \ No newline at end of file diff --git a/rust/Cargo.toml b/rust/Cargo.toml new file mode 100644 index 000000000..e730ee830 --- /dev/null +++ b/rust/Cargo.toml @@ -0,0 +1,6 @@ +[package] +name = "rust" +version = "0.1.0" +edition = "2018" + +[dependencies] diff --git a/rust/Makefile.am b/rust/Makefile.am new file mode 100644 index 000000000..e8bf27894 --- /dev/null +++ b/rust/Makefile.am @@ -0,0 +1,29 @@ +# 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/lib.rs + +EXTRA_DIST = \ + $(generator_built) + +if HAVE_RUST + +endif + diff --git a/rust/src/.gitkeep b/rust/src/.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 -- 2.20.1 (Apple Git-117)
Hiroyuki Katsura
2019-Jul-02 13:14 UTC
[Libguestfs] [PATCH 02/12] Rust bindings: Add create / close functions
From: Hiroyuki_Katsura <hiroyuki.katsura.0513@gmail.com> --- generator/rust.ml | 90 ++++++++++++++++++++++++++++++++++++++++++++++- run.in | 9 +++++ rust/Cargo.toml | 2 +- 3 files changed, 99 insertions(+), 2 deletions(-) diff --git a/generator/rust.ml b/generator/rust.ml index 40ca8d198..d235dbefe 100644 --- a/generator/rust.ml +++ b/generator/rust.ml @@ -30,5 +30,93 @@ open C open Events let generate_rust () - generate_header CStyle LGPLv2plus + generate_header CStyle LGPLv2plus; + + pr " +#[allow(non_camel_case_types)] +enum guestfs_h {} + +#[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); +} + +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 + } +} + +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 }) + } + } +} + " 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/Cargo.toml b/rust/Cargo.toml index e730ee830..4ea7c299b 100644 --- a/rust/Cargo.toml +++ b/rust/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "rust" +name = "guestfs" version = "0.1.0" edition = "2018" -- 2.20.1 (Apple Git-117)
Hiroyuki Katsura
2019-Jul-02 13:14 UTC
[Libguestfs] [PATCH 03/12] Rust bindings: Add 4 bindings tests
--- rust/tests/010_load.rs | 24 +++++++++++++++++++ rust/tests/020_create.rs | 24 +++++++++++++++++++ rust/tests/030_create_flags.rs | 30 ++++++++++++++++++++++++ rust/tests/040_create_multiple.rs | 38 +++++++++++++++++++++++++++++++ 4 files changed, 116 insertions(+) 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 diff --git a/rust/tests/010_load.rs b/rust/tests/010_load.rs new file mode 100644 index 000000000..eadd78896 --- /dev/null +++ b/rust/tests/010_load.rs @@ -0,0 +1,24 @@ +/* libguestfs Python 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..0b57b19d7 --- /dev/null +++ b/rust/tests/020_create.rs @@ -0,0 +1,24 @@ +/* libguestfs Python 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..5de0589c1 --- /dev/null +++ b/rust/tests/030_create_flags.rs @@ -0,0 +1,30 @@ +/* libguestfs Python 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..ee481c278 --- /dev/null +++ b/rust/tests/040_create_multiple.rs @@ -0,0 +1,38 @@ +/* libguestfs Python 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) +} -- 2.20.1 (Apple Git-117)
Hiroyuki Katsura
2019-Jul-02 13:14 UTC
[Libguestfs] [PATCH 04/12] Rust bindings: Add generator of structs
From: Hiroyuki_Katsura <hiroyuki.katsura.0513@gmail.com> --- generator/rust.ml | 87 +++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 85 insertions(+), 2 deletions(-) diff --git a/generator/rust.ml b/generator/rust.ml index d235dbefe..0b0069e0a 100644 --- a/generator/rust.ml +++ b/generator/rust.ml @@ -29,12 +29,20 @@ open Structs open C open Events +let rec indent n = match n with + | x when x > 0 -> pr " "; indent (x - 1) + | _ -> () + let generate_rust () generate_header CStyle LGPLv2plus; pr " +use std::ffi; +use std::slice; +use std::os::raw::c_char; + #[allow(non_camel_case_types)] -enum guestfs_h {} +enum guestfs_h {} // opaque struct #[link(name = \"guestfs\")] extern \"C\" { @@ -118,5 +126,80 @@ impl Handle { } } } - " + +pub struct UUID { + uuid: [u8; 32] +} + +impl UUID { + fn new(uuid: [u8; 32]) -> UUID { + UUID { 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 " %s: i8,\n" n + | n, FString -> pr " %s: String,\n" n + | n, FBuffer -> pr " %s: Vec<u8>,\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, FUUID -> pr " %s: UUID,\n" n + | n, FOptPercent -> pr " %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 %s {\n" name; + pr " fn new(raw: *const Raw%s) -> %s {\n" name name; + pr " unsafe { %s {\n" name; + List.iter ( + fun x -> + indent 3; + match x with + | n, FChar -> + pr "%s: (*raw).%s as i8,\n" n n; + | n, FString -> + pr "%s: {\n" n; + indent 4; + pr "let s = ffi::CStr::from_ptr((*raw).%s);\n" 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 + | 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 { None } else { Some((*raw).%s) },\n" n n n + ) cols; + pr " } }\n"; + pr " }\n"; + pr "}\n" + ) external_structs; -- 2.20.1 (Apple Git-117)
Hiroyuki Katsura
2019-Jul-02 13:14 UTC
[Libguestfs] [PATCH 05/12] Rust bindings: Add generator of structs for optional arguments
From: Hiroyuki_Katsura <hiroyuki.katsura.0513@gmail.com> --- generator/rust.ml | 71 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 71 insertions(+) diff --git a/generator/rust.ml b/generator/rust.ml index 0b0069e0a..1b801c0fe 100644 --- a/generator/rust.ml +++ b/generator/rust.ml @@ -29,10 +29,31 @@ 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; @@ -203,3 +224,53 @@ impl UUID { pr "}\n" ) external_structs; + List.iter ( + fun ({ name = name; shortdesc = shortdesc; + style = (ret, args, optargs) }) -> + let cname = snake2caml name in + if optargs <> [] then ( + pr "\n"; + pr "/* Optional Structs */\n"; + pr "#[derive(Default)]\n"; + pr "pub struct OptArgs%s {\n" cname; + List.iter ( + fun optarg -> + let n = translate_bad_symbols (name_of_optargt optarg) in + match optarg with + | OBool _ -> + pr " _%s: Option<bool>,\n" n + | OInt _ -> + pr " _%s: Option<i32>,\n" n + | OInt64 _ -> + pr " _%s: Option<i64>,\n" n + | OString _ -> + pr " _%s: Option<String>,\n" n + | OStringList _ -> + pr " _%s: Option<Vec<String>>,\n" n + ) optargs; + pr "}\n\n"; + pr "impl OptArgs%s {\n" cname; + 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 "String" + | OStringList _ -> + pr "Vec<String>" + ); + pr ") -> OptArgs%s {\n" cname; + pr " OptArgs%s { _%s: Some(%s), ..self }\n" cname n n; + pr " }\n" + ) optargs; + pr "}\n\n"; + ); + ) (actions |> external_functions |> sort); + -- 2.20.1 (Apple Git-117)
Hiroyuki Katsura
2019-Jul-02 13:14 UTC
[Libguestfs] [PATCH 06/12] Rust bindings: Add generator of function signatures
From: Hiroyuki_Katsura <hiroyuki.katsura.0513@gmail.com> --- generator/rust.ml | 135 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 135 insertions(+) diff --git a/generator/rust.ml b/generator/rust.ml index 1b801c0fe..58e10d24d 100644 --- a/generator/rust.ml +++ b/generator/rust.ml @@ -58,8 +58,10 @@ let generate_rust () generate_header CStyle LGPLv2plus; pr " +use std::collections; use std::ffi; use std::slice; +use std::ptr; use std::os::raw::c_char; #[allow(non_camel_case_types)] @@ -128,6 +130,18 @@ impl CreateFlags { } } +fn arg_string_list (v: &Vec<String>) -> Vec<*const i8> { + let length = v.len(); + 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(ptr::null()); + w +} + impl Handle { pub fn create() -> Result<Handle, &'static str> { let g = unsafe { guestfs_create() }; @@ -148,6 +162,10 @@ impl Handle { } } +pub struct Error { + // TODO +} + pub struct UUID { uuid: [u8; 32] } @@ -274,3 +292,120 @@ impl UUID { ); ) (actions |> external_functions |> sort); + pr "impl Handle {\n"; + List.iter ( + fun ({ name = name; shortdesc = shortdesc; + style = (ret, args, optargs) } as f) -> + let cname = snake2caml name in + pr " /// %s \n" shortdesc; + 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: String" n + | OptString n -> pr "%s: Option<String>" n + | StringList (_, n) -> pr "%s: Vec<String>" n + | BufferIn n -> pr "%s: Vec<u8>" n + | Pointer (_, n) -> pr "%s: usize" n + ) args; + if optargs <> [] then ( + if !comma then pr ", "; + comma := true; + pr "optargs: OptArgs%s" cname + ); + pr ")"; + + (* generate return type *) + pr " -> Result<"; + (match ret with + | RErr -> pr "()" + | RInt _ -> pr "i32" + | RInt64 _ -> pr "i64" + | RBool _ -> pr "bool" + | RConstString _ + | RString _ -> pr "String" + | RConstOptString _ -> pr "Option<String>" + | 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 c_%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; + | 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"; + | StringList (_, n) -> + pr "let c_%s_v = arg_string_list(%s);\n" n n; + | BufferIn 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; + | Int _ | Int64 _ | Pointer _ -> () + ) args; + + (* TODO: handle optargs *) + if optargs <> [] then ( + ); + + (match ret with + | RBufferOut _ -> + pr "let mut size = 0;\n" + | _ -> () + ); + + pr "\n"; + + pr "let r = unsafe { %s(self.g" f.c_function; + List.iter ( + 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 + ) args; + (match ret with + | RBufferOut _ -> pr ", &size as *const usize" + | _ -> () + ); + pr ") };\n"; + + pr "unimplemented!()\n"; + let pr = _pr in + + pr " }\n\n" + ) (actions |> external_functions |> sort); + pr "}\n" + -- 2.20.1 (Apple Git-117)
Hiroyuki Katsura
2019-Jul-02 13:14 UTC
[Libguestfs] [PATCH 07/12] Rust bindings: Complete actions
From: Hiroyuki_Katsura <hiroyuki.katsura.0513@gmail.com> --- generator/rust.ml | 282 ++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 259 insertions(+), 23 deletions(-) diff --git a/generator/rust.ml b/generator/rust.ml index 58e10d24d..d0add9b3d 100644 --- a/generator/rust.ml +++ b/generator/rust.ml @@ -59,10 +59,11 @@ let generate_rust () pr " use std::collections; +use std::convert; use std::ffi; use std::slice; use std::ptr; -use std::os::raw::c_char; +use std::os::raw::{c_char, c_int}; #[allow(non_camel_case_types)] enum guestfs_h {} // opaque struct @@ -72,6 +73,8 @@ 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; } const GUESTFS_CREATE_NO_ENVIRONMENT: i64 = 1; @@ -130,6 +133,29 @@ impl CreateFlags { } } +struct NullTerminatedIter<T: Copy + Clone> { + p: *const T +} + +impl<T: Copy + Clone> NullTerminatedIter<T> { + fn new(p: *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() { + None + } else { + let r = unsafe { *(self.p) }; + self.p = unsafe { self.p.offset(1) }; + Some(r) + } + } +} + fn arg_string_list (v: &Vec<String>) -> Vec<*const i8> { let length = v.len(); let mut w = Vec::new(); @@ -142,6 +168,45 @@ fn arg_string_list (v: &Vec<String>) -> Vec<*const i8> { w } +fn hashmap (l: *const *const c_char) -> collections::HashMap<String, String> { + 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(); + map.insert(key.to_string(), val.to_string()); + } else { + panic!(\"odd number of items in hash table\"); + } + } + map +} + +fn struct_list<T, S: convert::From<*const T>>(l: *const *const T) -> Vec<S> { + let mut v = Vec::new(); + for x in NullTerminatedIter::new(l) { + v.push(S::from(x)); + } + v +} + +fn string_list (l: *const *const c_char) -> Vec<String> { + let mut v = Vec::new(); + for x in NullTerminatedIter::new(l) { + let s = unsafe { ffi::CStr::from_ptr(x) }.to_str().unwrap(); + v.push(s.to_string()); + } + v +} + +#[derive(Debug)] +pub struct Error { + operation: &'static str, + message: String, + errno: i32 +} + impl Handle { pub fn create() -> Result<Handle, &'static str> { let g = unsafe { guestfs_create() }; @@ -160,10 +225,13 @@ impl Handle { Ok(Handle { g }) } } -} -pub struct Error { - // TODO + 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 } + } } pub struct UUID { @@ -174,6 +242,9 @@ impl UUID { fn new(uuid: [u8; 32]) -> UUID { UUID { uuid } } + pub fn to_bytes(self) -> [u8; 32] { + self.uuid + } } "; List.iter ( @@ -182,15 +253,15 @@ impl UUID { pr "pub struct %s {\n" name; List.iter ( function - | n, FChar -> pr " %s: i8,\n" n - | n, FString -> pr " %s: String,\n" n - | n, FBuffer -> pr " %s: Vec<u8>,\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, FUUID -> pr " %s: UUID,\n" n - | n, FOptPercent -> pr " %s: Option<f32>,\n" n + | 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"; @@ -211,8 +282,8 @@ impl UUID { ) cols; pr "}\n"; pr "\n"; - pr "impl %s {\n" name; - pr " fn new(raw: *const Raw%s) -> %s {\n" name name; + 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; List.iter ( fun x -> @@ -267,6 +338,71 @@ impl UUID { pr " _%s: Option<Vec<String>>,\n" n ) optargs; pr "}\n\n"; + + (* raw struct for C bindings *) + pr "#[repr(C)]\n"; + pr "struct RawOptArgs%s {\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<OptArgs%s> for RawOptArgs%s {\n" cname cname; + pr " fn from(optargs: OptArgs%s) -> Self {\n" cname; + pr " let mut bitmask = 0;\n"; + pr " RawOptArgs%s {\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; + 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 " bitmask |= 1 << %d;\n" index; + pr " let y: &str = &v;\n"; + pr " ffi::CString::new(y).unwrap().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 " bitmask |= 1 << %d;\n" index; + pr " arg_string_list(&v).as_ptr()"; + pr " } else {\n"; + pr " ptr::null()\n"; + pr " },\n"; + ) optargs; + pr " bitmask,\n"; + pr " }\n"; + pr " }\n"; + pr "}\n"; + pr "impl OptArgs%s {\n" cname; List.iter ( fun optarg -> @@ -292,6 +428,54 @@ impl UUID { ); ) (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 "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 RawOptArgs%s" 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 *const Raw%s" n + ); + pr ";\n"; + + ) (actions |> external_functions |> sort); + pr "}\n"; + + pr "impl Handle {\n"; List.iter ( fun ({ name = name; shortdesc = shortdesc; @@ -331,9 +515,9 @@ impl UUID { | RInt _ -> pr "i32" | RInt64 _ -> pr "i64" | RBool _ -> pr "bool" - | RConstString _ + | RConstString _ -> pr "&'static str" | RString _ -> pr "String" - | RConstOptString _ -> pr "Option<String>" + | RConstOptString _ -> pr "Option<&'static str>" | RStringList _ -> pr "Vec<String>" | RStruct (_, sn) -> let sn = camel_name_of_struct sn in @@ -364,28 +548,30 @@ impl UUID { pr " None => ptr::null(),\n"; pr "};\n"; | StringList (_, n) -> - pr "let c_%s_v = arg_string_list(%s);\n" n n; + pr "let c_%s = arg_string_list(&%s).as_ptr();\n" 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; | 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 = 0;\n" + pr "let mut size = 0usize;\n" | _ -> () ); pr "\n"; pr "let r = unsafe { %s(self.g" f.c_function; + let pr = _pr in List.iter ( fun arg -> pr ", "; @@ -393,18 +579,68 @@ impl UUID { | 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 + | StringList (_, n) -> pr "c_%s as *const *const c_char" n | BufferIn n -> pr "c_%s, c_%s_len" n n ) args; (match ret with | RBufferOut _ -> pr ", &size as *const usize" | _ -> () ); + if optargs <> [] then ( + pr ", &optargs_raw as *const RawOptArgs%s" cname; + ); pr ") };\n"; - pr "unimplemented!()\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" + ); + pr "Ok("; let pr = _pr 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" + | RString _ -> + (* TODO: free r *) + pr "unsafe { ffi::CStr::from_ptr(r) }.to_str().unwrap().to_string()" + | RConstOptString _ -> + (* TODO: free ? not? *) + 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 "}"; + | RStringList _ -> + (* TODO: free r *) + pr "string_list(r)" + | RStruct (_, n) -> + (* TODO: free r *) + let n = camel_name_of_struct n in + pr "%s::from(r)" n + | RStructList (_, n) -> + (* TODO: free r *) + let n = camel_name_of_struct n in + pr "struct_list::<Raw%s, %s>(r)" n n + | RHashtable _ -> + (* TODO: free r *) + pr "hashmap(r)" + | RBufferOut _ -> + (* TODO: free r *) + pr "unsafe { slice::from_raw_parts(r, size) }.to_vec()" + ); + pr ")\n"; pr " }\n\n" ) (actions |> external_functions |> sort); pr "}\n" -- 2.20.1 (Apple Git-117)
Hiroyuki Katsura
2019-Jul-02 13:14 UTC
[Libguestfs] [PATCH 08/12] Rust bindings: Fix memory management
--- generator/rust.ml | 460 +++++++++++++++++++++++++++++++--------------- 1 file changed, 310 insertions(+), 150 deletions(-) diff --git a/generator/rust.ml b/generator/rust.ml index d0add9b3d..f8ed20023 100644 --- a/generator/rust.ml +++ b/generator/rust.ml @@ -60,13 +60,15 @@ let generate_rust () pr " use std::collections; use std::convert; +use std::convert::TryFrom; use std::ffi; -use std::slice; +use std::os::raw::{c_char, c_int, c_void}; use std::ptr; -use std::os::raw::{c_char, c_int}; +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\" { @@ -77,6 +79,10 @@ extern \"C\" { 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; @@ -134,77 +140,136 @@ 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) } } } -fn arg_string_list (v: &Vec<String>) -> Vec<*const i8> { - let length = v.len(); +#[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; - 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 *const 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 NullTerminatedIter::new(l) { - 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 + 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 { @@ -229,13 +294,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 { @@ -282,22 +351,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 @@ -306,42 +377,128 @@ 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; + (* 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 OptArgs%s {\n" cname; + 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 " 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 + pr " %s: Option<i64>,\n" n | OString _ -> - pr " _%s: Option<String>,\n" n + pr " %s: Option<ffi::CString>,\n" n | OStringList _ -> - pr " _%s: Option<Vec<String>>,\n" n + (* 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 -> @@ -360,40 +517,33 @@ impl UUID { ) optargs; pr "}\n\n"; - pr "impl convert::From<OptArgs%s> for RawOptArgs%s {\n" cname cname; - pr " fn from(optargs: OptArgs%s) -> Self {\n" cname; + 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"; @@ -402,29 +552,6 @@ impl UUID { pr " }\n"; pr " }\n"; pr "}\n"; - - pr "impl OptArgs%s {\n" cname; - 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 "String" - | OStringList _ -> - pr "Vec<String>" - ); - pr ") -> OptArgs%s {\n" cname; - pr " OptArgs%s { _%s: Some(%s), ..self }\n" cname n n; - pr " }\n" - ) optargs; - pr "}\n\n"; ); ) (actions |> external_functions |> sort); @@ -434,7 +561,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 ", "; @@ -453,7 +581,7 @@ impl UUID { | _ -> () ); if optargs <> [] then - pr ", optarg: *const RawOptArgs%s" cname; + pr ", optarg: *const Raw%sOptArgs" cname; pr ") -> "; @@ -468,7 +596,7 @@ impl UUID { pr "*const Raw%s" n | RStructList (_, n) -> let n = camel_name_of_struct n in - pr "*const *const Raw%s" n + pr "*const RawList<Raw%s>" n ); pr ";\n"; @@ -478,14 +606,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 -> @@ -495,16 +625,16 @@ impl UUID { | Bool n -> pr "%s: bool" n | Int n -> pr "%s: i32" n | Int64 n -> pr "%s: i64" n - | String (_, n) -> pr "%s: String" n - | OptString n -> pr "%s: Option<String>" n - | StringList (_, n) -> pr "%s: Vec<String>" n - | BufferIn n -> pr "%s: Vec<u8>" n - | Pointer (_, n) -> pr "%s: usize" 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: OptArgs%s" cname + pr "optargs: %sOptArgs" cname ); pr ")"; @@ -529,45 +659,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; @@ -576,18 +698,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"; @@ -597,48 +720,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 _ -> - (* TODO: free r *) - pr "unsafe { ffi::CStr::from_ptr(r) }.to_str().unwrap().to_string()" + 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 _ -> - (* TODO: free ? not? *) - 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 _ -> - (* TODO: free r *) - pr "string_list(r)" + pr "{\n"; + pr3 "let s = string_list(r);\n"; + pr3 "free_string_list(r);\n"; + pr3 "s?\n"; + indent 2; pr "}"; | RStruct (_, n) -> - (* TODO: free r *) - let n = camel_name_of_struct n in - pr "%s::from(r)" 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) -> - (* TODO: free r *) - let n = camel_name_of_struct n in - pr "struct_list::<Raw%s, %s>(r)" n 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 _ -> - (* TODO: free r *) - pr "hashmap(r)" + pr "{\n"; + pr3 "let h = hashmap(r);\n"; + pr3 "free_string_list(r);\n"; + pr3 "h?\n"; + indent 2; pr "}"; | RBufferOut _ -> - (* TODO: free r *) - pr "unsafe { slice::from_raw_parts(r, size) }.to_vec()" + 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" -- 2.20.1 (Apple Git-117)
Hiroyuki Katsura
2019-Jul-02 13:14 UTC
[Libguestfs] [PATCH 09/12] Rust bindings: Add bindtests
--- generator/bindtests.ml | 65 +++++++++++++++++++++++++++++++++++++++++- generator/main.ml | 2 +- generator/rust.mli | 5 +++- rust/src/bin/.gitkeep | 0 4 files changed, 69 insertions(+), 3 deletions(-) create mode 100644 rust/src/bin/.gitkeep diff --git a/generator/bindtests.ml b/generator/bindtests.ml index 8a5682d5e..e88e71c8a 100644 --- a/generator/bindtests.ml +++ b/generator/bindtests.ml @@ -984,7 +984,70 @@ and generate_php_bindtests () dump "bindtests" and generate_rust_bindtests () - generate_header CStyle GPLv2plus + 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.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/src/bin/.gitkeep b/rust/src/bin/.gitkeep new file mode 100644 index 000000000..e69de29bb -- 2.20.1 (Apple Git-117)
Hiroyuki Katsura
2019-Jul-02 13:14 UTC
[Libguestfs] [PATCH 10/12] Rust bindings: Add additional 4 bindings tests
--- rust/tests/050_handle_properties.rs | 61 +++++++++++++++++++++++++++ rust/tests/070_opt_args.rs | 36 ++++++++++++++++ rust/tests/080_version.rs | 29 +++++++++++++ rust/tests/100_launch.rs | 65 +++++++++++++++++++++++++++++ 4 files changed, 191 insertions(+) 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/100_launch.rs diff --git a/rust/tests/050_handle_properties.rs b/rust/tests/050_handle_properties.rs new file mode 100644 index 000000000..3c7b449f9 --- /dev/null +++ b/rust/tests/050_handle_properties.rs @@ -0,0 +1,61 @@ +/* libguestfs Python 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..241c95b35 --- /dev/null +++ b/rust/tests/070_opt_args.rs @@ -0,0 +1,36 @@ +/* libguestfs Python 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::OptArgsAddDrive::default().readonly(true)) + .expect("add_drive"); +} diff --git a/rust/tests/080_version.rs b/rust/tests/080_version.rs new file mode 100644 index 000000000..ab5314edc --- /dev/null +++ b/rust/tests/080_version.rs @@ -0,0 +1,29 @@ +/* libguestfs Python 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 version() { + let g = guestfs::Handle::create().expect("create"); + let v = g.version().expect("version"); + assert_eq!(v.major, 1) +} + 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)
Hiroyuki Katsura
2019-Jul-02 13:14 UTC
[Libguestfs] [PATCH 11/12] Rust bindings: Format test files
--- 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 +++++++++++++++++++++++++++++ 8 files changed, 169 insertions(+), 106 deletions(-) create mode 100644 rust/tests/090_ret_values.rs 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) +} -- 2.20.1 (Apple Git-117)
Hiroyuki Katsura
2019-Jul-02 13:14 UTC
[Libguestfs] [PATCH 12/12] Rust bindings: Incorporate bindings to build system
--- configure.ac | 2 ++ rust/.gitignore | 2 ++ rust/Makefile.am | 16 +++++++++++++++- rust/run-bindtests | 23 +++++++++++++++++++++++ rust/run-tests | 21 +++++++++++++++++++++ 5 files changed, 63 insertions(+), 1 deletion(-) create mode 100644 rust/.gitignore create mode 100755 rust/run-bindtests create mode 100755 rust/run-tests 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/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 -- 2.20.1 (Apple Git-117)
On Tue, Jul 02, 2019 at 10:09:00PM +0900, Hiroyuki Katsura wrote:> I fixed the patch I submitted before based on comments, and there are some > commits which are merged or divided. So, I will re-send all the patches.Yes, that's the right thing to do, thanks. Rich. -- Richard Jones, Virtualization Group, Red Hat http://people.redhat.com/~rjones Read my programming and virtualization blog: http://rwmj.wordpress.com libguestfs lets you edit virtual machines. Supports shell scripting, bindings from many languages. http://libguestfs.org
Martin Kletzander
2019-Jul-03 07:58 UTC
Re: [Libguestfs] [PATCH 01/12] Rust bindings: Add Rust bindings
On Tue, Jul 02, 2019 at 10:14:19PM +0900, Hiroyuki Katsura wrote:>From: Hiroyuki_Katsura <hiroyuki.katsura.0513@gmail.com> > >--- > Makefile.am | 4 ++++ > configure.ac | 3 +++ > generator/Makefile.am | 3 +++ > generator/bindtests.ml | 3 +++ > generator/bindtests.mli | 1 + > generator/main.ml | 5 +++++ > generator/rust.ml | 34 ++++++++++++++++++++++++++++++++++ > generator/rust.mli | 19 +++++++++++++++++++ > m4/guestfs-rust.m4 | 30 ++++++++++++++++++++++++++++++ > rust/Cargo.toml | 6 ++++++ > rust/Makefile.am | 29 +++++++++++++++++++++++++++++ > rust/src/.gitkeep | 0 > rust/tests/.gitkeep | 0 > 13 files changed, 137 insertions(+) > create mode 100644 generator/rust.ml > create mode 100644 generator/rust.mli > create mode 100644 m4/guestfs-rust.m4 > create mode 100644 rust/Cargo.toml > create mode 100644 rust/Makefile.am > create mode 100644 rust/src/.gitkeep > create mode 100644 rust/tests/.gitkeep >[...]>diff --git a/generator/rust.mli b/generator/rust.mli >new file mode 100644 >index 000000000..4fef55d4e >--- /dev/null >+++ b/generator/rust.mli >@@ -0,0 +1,19 @@ >+(* 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 >\ No newline at end of fileI'm not sure what editor you use, but either setting it up so that there is a '\n' in the end of the file (at least by default unless you remove it or something) would probably be favourable.>diff --git a/m4/guestfs-rust.m4 b/m4/guestfs-rust.m4 >new file mode 100644 >index 000000000..f46ce960b >--- /dev/null >+++ b/m4/guestfs-rust.m4 >@@ -0,0 +1,30 @@ >+# 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]) >+],[ >+ RUSTC=no >+ CARGO=no >+ ]) >+AM_CONDITIONAL([HAVE_RUST],[test "x$RUSTC" != "xno" && test "x$CARGO" != "xno"])So even if someone explicitly specifies --enable-rust without having the tools, they will not get the bindings built, but they will also not get an error nor confirmation in the configure output, so it is really hard to figure out what is happening. I, personally, prefer the usual `--enable-rust=yes|check|no` semantics. Of course this can be fixed later, do not let this rust bindings-unrelated review stop others from reviewing the important parts ;) [...]>diff --git a/rust/Makefile.am b/rust/Makefile.am >new file mode 100644 >index 000000000..e8bf27894 >--- /dev/null >+++ b/rust/Makefile.am >@@ -0,0 +1,29 @@ >+# 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/lib.rs >+ >+EXTRA_DIST = \ >+ $(generator_built) >+ >+if HAVE_RUST >+ >+endifMaybe something is missing here? Martin
On Tue, Jul 02, 2019 at 10:09:00PM +0900, Hiroyuki Katsura wrote:> I fixed the patch I submitted before based on comments, and there are some > commits which are merged or divided. So, I will re-send all the patches.I looked at the v2 patches and I think they're in reasonable shape. There's been a lot of discussion of using ’git rebase --interactive’. I think that's a good thing, and also good practice if you're going to become a more frequent open source contributor. It helps people when they're reviewing patches. For this particular case I think we'd probably squash all the patches into a single commit when pushing them upstream. Have you decided whether we need to commit the generated files (Cargo.toml, Cargo.lock)? It looks like in this series those files are still included and not added to .gitignore. Rich. -- Richard Jones, Virtualization Group, Red Hat http://people.redhat.com/~rjones Read my programming and virtualization blog: http://rwmj.wordpress.com virt-builder quickly builds VMs from scratch http://libguestfs.org/virt-builder.1.html
> Have you decided whether we need to commit the generated files > (Cargo.toml, Cargo.lock)? It looks like in this series those files > are still included and not added to .gitignore.I'm sorry I forgot adding Cargo.lock to .gitignore. I'll add it to .gitignore. However, I think Cargo.toml should be staged. This is because this is a file managed by hands. It contains dependencies, versions, editions, crate name. It can be generated by shellscript which dumps such information. However, I think it may be not preferable. Regards, Hiroyuki 2019年7月4日(木) 19:39 Richard W.M. Jones <rjones@redhat.com>:> On Tue, Jul 02, 2019 at 10:09:00PM +0900, Hiroyuki Katsura wrote: > > I fixed the patch I submitted before based on comments, and there are > some > > commits which are merged or divided. So, I will re-send all the patches. > > I looked at the v2 patches and I think they're in reasonable > shape. > > There's been a lot of discussion of using ’git rebase --interactive’. > I think that's a good thing, and also good practice if you're going to > become a more frequent open source contributor. It helps people when > they're reviewing patches. > > For this particular case I think we'd probably squash all the patches > into a single commit when pushing them upstream. > > Have you decided whether we need to commit the generated files > (Cargo.toml, Cargo.lock)? It looks like in this series those files > are still included and not added to .gitignore. > > Rich. > > -- > Richard Jones, Virtualization Group, Red Hat > http://people.redhat.com/~rjones > Read my programming and virtualization blog: http://rwmj.wordpress.com > virt-builder quickly builds VMs from scratch > http://libguestfs.org/virt-builder.1.html >