Hiroyuki Katsura
2019-Jun-27 08:06 UTC
[Libguestfs] [PATCH 1/9] 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..41aef47ea 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..83afdfe73 --- /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; \ No newline at end of file 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-Jun-27 08:06 UTC
[Libguestfs] [PATCH 2/9] Rust bindings: Add create / close functions
From: Hiroyuki_Katsura <hiroyuki.katsura.0513@gmail.com> --- generator/rust.ml | 84 ++++++++++++++++++++++++++++++++++++++++++++++- rust/Cargo.toml | 2 ++ 2 files changed, 85 insertions(+), 1 deletion(-) diff --git a/generator/rust.ml b/generator/rust.ml index 83afdfe73..dbe9db010 100644 --- a/generator/rust.ml +++ b/generator/rust.ml @@ -31,4 +31,86 @@ open Events let generate_rust () - generate_header CStyle LGPLv2plus; \ No newline at end of file + generate_header CStyle LGPLv2plus; + + pr " +#[allow(non_camel_case_types)] +enum guestfs_h {} + +extern \"C\" { + fn guestfs_create() -> *mut guestfs_h; + fn guestfs_create_flags(flags: i64) -> *mut guestfs_h; + fn guestfs_close(g: *mut guestfs_h); + static GUESTFS_CREATE_NO_ENVIRONMENT: i64; + static GUESTFS_CREATE_NO_CLOSE_ON_EXIT: i64; +} + +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 new() -> CreateFlags { + CreateFlags { + create_no_environment_flag: false, + create_no_close_on_exit_flag: false, + } + } + + 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/rust/Cargo.toml b/rust/Cargo.toml index e730ee830..6cd94ce6a 100644 --- a/rust/Cargo.toml +++ b/rust/Cargo.toml @@ -4,3 +4,5 @@ version = "0.1.0" edition = "2018" [dependencies] +libc = "0.2" + -- 2.20.1 (Apple Git-117)
Hiroyuki Katsura
2019-Jun-27 08:06 UTC
[Libguestfs] [PATCH 3/9] Rust bindings: Add 4 bindings tests
From: Hiroyuki_Katsura <hiroyuki.katsura.0513@gmail.com> --- generator/rust.ml | 13 ++++++++--- run.in | 9 ++++++++ rust/Cargo.lock | 6 +++++ rust/Cargo.toml | 4 +--- 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 +++++++++++++++++++++++++++++++ 8 files changed, 142 insertions(+), 6 deletions(-) create mode 100644 rust/Cargo.lock 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/generator/rust.ml b/generator/rust.ml index dbe9db010..251eb1594 100644 --- a/generator/rust.ml +++ b/generator/rust.ml @@ -37,14 +37,16 @@ let generate_rust () #[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); - static GUESTFS_CREATE_NO_ENVIRONMENT: i64; - static GUESTFS_CREATE_NO_CLOSE_ON_EXIT: i64; } +const GUESTFS_CREATE_NO_ENVIRONMENT: i64 = 1; +const GUESTFS_CREATE_NO_CLOSE_ON_EXIT: i64 = 2; + pub struct Handle { g: *mut guestfs_h, } @@ -61,13 +63,17 @@ pub struct CreateFlags { } impl CreateFlags { - pub fn new() -> 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 @@ -97,6 +103,7 @@ impl CreateFlags { impl Handle { pub fn create() -> Result<Handle, &'static str> { let g = unsafe { guestfs_create() }; + println!(\"hoge\"); if g.is_null() { Err(\"failed to create guestfs handle\") } else { 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.lock b/rust/Cargo.lock new file mode 100644 index 000000000..c03586e3f --- /dev/null +++ b/rust/Cargo.lock @@ -0,0 +1,6 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +[[package]] +name = "guestfs" +version = "0.1.0" + diff --git a/rust/Cargo.toml b/rust/Cargo.toml index 6cd94ce6a..4ea7c299b 100644 --- a/rust/Cargo.toml +++ b/rust/Cargo.toml @@ -1,8 +1,6 @@ [package] -name = "rust" +name = "guestfs" version = "0.1.0" edition = "2018" [dependencies] -libc = "0.2" - 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-Jun-27 08:06 UTC
[Libguestfs] [PATCH 4/9] 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 251eb1594..174f6ded3 100644 --- a/generator/rust.ml +++ b/generator/rust.ml @@ -29,13 +29,21 @@ 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\" { @@ -119,5 +127,80 @@ impl Handle { Ok(Handle { g }) } } +}\ + +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-Jun-27 08:06 UTC
[Libguestfs] [PATCH 5/9] 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 174f6ded3..a229d5eac 100644 --- a/generator/rust.ml +++ b/generator/rust.ml @@ -29,10 +29,32 @@ 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; @@ -204,3 +226,52 @@ impl UUID { pr " }\n"; 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-Jun-27 08:06 UTC
[Libguestfs] [PATCH 6/9] Rust bindings: Add generator of function signatures
From: Hiroyuki_Katsura <hiroyuki.katsura.0513@gmail.com> --- generator/rust.ml | 137 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 137 insertions(+) diff --git a/generator/rust.ml b/generator/rust.ml index a229d5eac..aa8b249ff 100644 --- a/generator/rust.ml +++ b/generator/rust.ml @@ -59,8 +59,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; @@ -130,6 +132,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() }; @@ -151,6 +165,10 @@ impl Handle { } }\ +pub struct Error { + // TODO +} + pub struct UUID { uuid: [u8; 32] } @@ -226,6 +244,8 @@ impl UUID { pr " }\n"; pr "}\n" ) external_structs; + + (* generate structs for optional arguments *) List.iter ( fun ({ name = name; shortdesc = shortdesc; style = (ret, args, optargs) }) -> @@ -275,3 +295,120 @@ impl UUID { pr "}\n\n"; ); ) (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-Jun-27 08:06 UTC
[Libguestfs] [PATCH 7/9] Rust bindings: Complete actions
From: Hiroyuki_Katsura <hiroyuki.katsura.0513@gmail.com> --- generator/rust.ml | 283 ++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 259 insertions(+), 24 deletions(-) diff --git a/generator/rust.ml b/generator/rust.ml index aa8b249ff..79e16dfc6 100644 --- a/generator/rust.ml +++ b/generator/rust.ml @@ -60,10 +60,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)] @@ -74,6 +75,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; @@ -132,6 +135,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(); @@ -144,10 +170,48 @@ 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() }; - println!(\"hoge\"); if g.is_null() { Err(\"failed to create guestfs handle\") } else { @@ -163,10 +227,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 { @@ -177,6 +244,9 @@ impl UUID { fn new(uuid: [u8; 32]) -> UUID { UUID { uuid } } + pub fn to_bytes(self) -> [u8; 32] { + self.uuid + } } "; List.iter ( @@ -185,15 +255,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"; @@ -214,8 +284,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 -> @@ -271,6 +341,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 -> @@ -296,6 +431,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; @@ -335,9 +518,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 @@ -368,28 +551,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 ", "; @@ -397,18 +582,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-Jun-27 08:06 UTC
[Libguestfs] [PATCH 8/9] Rust bindings: Free buffers
From: Hiroyuki_Katsura <hiroyuki.katsura.0513@gmail.com> --- generator/rust.ml | 142 +++++++++++++++++++++------- rust/tests/050_handle_properties.rs | 61 ++++++++++++ rust/tests/070_opt_args.rs | 36 +++++++ rust/tests/080_version.rs | 29 ++++++ 4 files changed, 236 insertions(+), 32 deletions(-) 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 diff --git a/generator/rust.ml b/generator/rust.ml index 79e16dfc6..4766357be 100644 --- a/generator/rust.ml +++ b/generator/rust.ml @@ -64,7 +64,7 @@ use std::convert; use std::ffi; use std::slice; use std::ptr; -use std::os::raw::{c_char, c_int}; +use std::os::raw::{c_char, c_int, c_void}; #[allow(non_camel_case_types)] @@ -79,6 +79,11 @@ 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; @@ -158,7 +163,38 @@ impl<T: Copy + Clone> Iterator for NullTerminatedIter<T> { } } -fn arg_string_list (v: &Vec<String>) -> Vec<*const i8> { +#[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: &Vec<&str>) -> Vec<*const i8> { let length = v.len(); let mut w = Vec::new(); for x in v.iter() { @@ -185,9 +221,9 @@ fn hashmap (l: *const *const c_char) -> collections::HashMap<String, String> { map } -fn struct_list<T, S: convert::From<*const T>>(l: *const *const T) -> Vec<S> { +fn struct_list<T, S: convert::From<*const T>>(l: *const RawList<T>) -> Vec<S> { let mut v = Vec::new(); - for x in NullTerminatedIter::new(l) { + for x in unsafe {&*l}.iter() { v.push(S::from(x)); } v @@ -202,6 +238,10 @@ fn string_list (l: *const *const c_char) -> Vec<String> { v } +fn free_string_list(l: *const *const c_char) { + // TODO +} + #[derive(Debug)] pub struct Error { operation: &'static str, @@ -248,6 +288,7 @@ impl UUID { self.uuid } } + "; List.iter ( fun { s_camel_name = name; s_name = c_name; s_cols = cols } -> @@ -315,16 +356,34 @@ impl UUID { 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 "fn guestfs_free_%s(v: *const Raw%s);\n" c_name name; + pr "fn guestfs_free_%s_list(l: *const RawList<Raw%s>);\n" c_name name; + ) external_structs; + pr "}\n"; + + (* 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 OptArgs%s%s {\n" cname opt_life_parameter; List.iter ( fun optarg -> let n = translate_bad_symbols (name_of_optargt optarg) in @@ -336,9 +395,9 @@ impl UUID { | OInt64 _ -> pr " _%s: Option<i64>,\n" n | OString _ -> - pr " _%s: Option<String>,\n" n + pr " _%s: Option<&'a str>,\n" n | OStringList _ -> - pr " _%s: Option<Vec<String>>,\n" n + pr " _%s: Option<Vec<&'a str>>,\n" n ) optargs; pr "}\n\n"; @@ -363,8 +422,9 @@ 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%s convert::From<OptArgs%s%s> for RawOptArgs%s {\n" + opt_life_parameter cname opt_life_parameter cname; + pr " fn from(optargs: OptArgs%s%s) -> Self {\n" cname opt_life_parameter; pr " let mut bitmask = 0;\n"; pr " RawOptArgs%s {\n" cname; List.iteri ( @@ -406,7 +466,7 @@ impl UUID { pr " }\n"; pr "}\n"; - pr "impl OptArgs%s {\n" cname; + pr "impl%s OptArgs%s%s {\n" opt_life_parameter cname opt_life_parameter; List.iter ( fun optarg -> let n = translate_bad_symbols (name_of_optargt optarg) in @@ -419,11 +479,11 @@ impl UUID { | OInt64 _ -> pr "i64" | OString _ -> - pr "String" + pr "&'a str" | OStringList _ -> - pr "Vec<String>" + pr "Vec<&'a str>" ); - pr ") -> OptArgs%s {\n" cname; + pr ") -> OptArgs%s%s {\n" cname opt_life_parameter; pr " OptArgs%s { _%s: Some(%s), ..self }\n" cname n n; pr " }\n" ) optargs; @@ -471,7 +531,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"; @@ -498,9 +558,9 @@ 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 + | String (_, n) -> pr "%s: &str" n + | OptString n -> pr "%s: Option<&str>" n + | StringList (_, n) -> pr "%s: Vec<&str>" n | BufferIn n -> pr "%s: Vec<u8>" n | Pointer (_, n) -> pr "%s: usize" n ) args; @@ -616,32 +676,50 @@ impl UUID { | 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()" + pr "{"; + pr " let s = unsafe {ffi::CStr::from_ptr(r)}\n"; + pr " .to_str().unwrap().to_string();\n"; + pr " unsafe { free(r as * const c_void) };\n"; + pr " s\n"; + pr "}\n"; | 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)" + pr "{\n"; + pr " let s = string_list(r);\n"; + pr " free_string_list(r);\n"; + pr " s\n"; + pr "}\n"; | 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"; + pr " let s = %s::from(r);\n" sn; + pr " unsafe { guestfs_free_%s(r) };\n" n; + pr " s\n"; + pr "}\n"; | 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"; + pr " let l = struct_list::<Raw%s, %s>(r);\n" sn sn; + pr " unsafe { guestfs_free_%s_list(r) };" n; + pr " l\n"; + pr "}\n"; | RHashtable _ -> - (* TODO: free r *) - pr "hashmap(r)" + pr "{\n"; + pr " let h = hashmap(r);\n"; + pr " free_string_list(r);\n"; + pr " h\n"; + pr "}\n"; | RBufferOut _ -> - (* TODO: free r *) - pr "unsafe { slice::from_raw_parts(r, size) }.to_vec()" + pr "{\n"; + pr " let s = unsafe { slice::from_raw_parts(r, size) }.to_vec();\n"; + pr " unsafe { free(r as * const c_void) } ;\n"; + pr " s\n"; + pr "}\n"; ); pr ")\n"; pr " }\n\n" 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) +} + -- 2.20.1 (Apple Git-117)
Hiroyuki Katsura
2019-Jun-27 08:06 UTC
[Libguestfs] [PATCH 9/9] Rust bindings: Complete bindings
From: Hiroyuki_Katsura <hiroyuki.katsura.0513@gmail.com> --- configure.ac | 2 + generator/bindtests.ml | 63 +++++ generator/main.ml | 2 +- generator/rust.ml | 423 +++++++++++++++++----------- generator/rust.mli | 5 +- rust/.gitignore | 2 + rust/Makefile.am | 16 +- rust/run-bindtests | 23 ++ rust/run-tests | 21 ++ rust/src/bin/.gitkeep | 0 rust/tests/010_load.rs | 28 +- rust/tests/020_create.rs | 28 +- rust/tests/030_create_flags.rs | 29 +- rust/tests/040_create_multiple.rs | 28 +- rust/tests/050_handle_properties.rs | 31 +- rust/tests/070_opt_args.rs | 39 +-- rust/tests/080_version.rs | 31 +- rust/tests/090_ret_values.rs | 61 ++++ rust/tests/100_launch.rs | 65 +++++ 19 files changed, 618 insertions(+), 279 deletions(-) create mode 100644 rust/.gitignore create mode 100755 rust/run-bindtests create mode 100755 rust/run-tests create mode 100644 rust/src/bin/.gitkeep create mode 100644 rust/tests/090_ret_values.rs create mode 100644 rust/tests/100_launch.rs diff --git a/configure.ac b/configure.ac index f9bdbe54b..b35b1ce0f 100644 --- a/configure.ac +++ b/configure.ac @@ -431,6 +431,8 @@ AS_ECHO_N(["Vala bindings ....................... "]) if test "x$ENABLE_VAPIGEN_TRUE" = "x"; then echo "yes"; else echo "no"; fi AS_ECHO_N(["bash completion ..................... "]) if test "x$HAVE_BASH_COMPLETION_TRUE" = "x"; then echo "yes"; else echo "no"; fi +AS_ECHO_N(["Rust bindings ....................... "]) +if test "x$HAVE_RUST_TRUE" = "x"; then echo "yes"; else echo "no"; fi echo echo "If any optional component is configured 'no' when you expected 'yes'" echo "then you should check the preceding messages." diff --git a/generator/bindtests.ml b/generator/bindtests.ml index 41aef47ea..e88e71c8a 100644 --- a/generator/bindtests.ml +++ b/generator/bindtests.ml @@ -986,6 +986,69 @@ and generate_php_bindtests () and generate_rust_bindtests () generate_header CStyle GPLv2plus; + pr "extern crate guestfs;\n"; + pr "use guestfs::*;\n"; + pr "use std::default::Default;\n"; + pr "\n"; + pr "fn main() {\n"; + pr " let g = match Handle::create() {\n"; + pr " Ok(g) => g,\n"; + pr " Err(e) => panic!(format!(\" could not create handle {}\", e)),\n"; + pr " };\n"; + generate_lang_bindtests ( + fun f args optargs -> + pr " g.%s(" f; + let needs_comma = ref false in + List.iter ( + fun arg -> + if !needs_comma then pr ", "; + needs_comma := true; + match arg with + | CallString s -> pr "\"%s\"" s + | CallOptString None -> pr "None" + | CallOptString (Some s) -> pr "Some(\"%s\")" s + | CallStringList xs -> + pr "&vec![%s]" + (String.concat ", " (List.map (sprintf "\"%s\"") xs)) + | CallInt i -> pr "%d" i + | CallInt64 i -> pr "%Ldi64" i + | CallBool b -> pr "%b" b + | CallBuffer s -> + let f = fun x -> sprintf "%d" (Char.code x) in + pr "&[%s]" + (String.concat ", " (List.map f (String.explode s))) + ) args; + if !needs_comma then pr ", "; + (match optargs with + | None -> pr "Default::default()" + | Some optargs -> + pr "%sOptArgs{" (Rust.snake2caml f); + needs_comma := false; + List.iter ( + fun optarg -> + if !needs_comma then pr ", "; + needs_comma := true; + match optarg with + | CallOBool (n, v) -> + pr "%s: Some(%b)" n v + | CallOInt (n, v) -> + pr "%s: Some(%d)" n v + | CallOInt64 (n, v) -> + pr "%s: Some(%Ldi64)" n v + | CallOString (n, v) -> + pr "%s: Some(\"%s\")" n v + | CallOStringList (n, xs) -> + pr "%s: Some(&[%s])" + n (String.concat ", " (List.map (sprintf "\"%s\"") xs)) + ) optargs; + if !needs_comma then pr ", "; + pr ".. Default::default()}"; + ); + pr ").expect(\"failed to run\");\n"; + ); + pr " println!(\"EOF\");\n"; + pr "}\n"; + (* Language-independent bindings tests - we do it this way to * ensure there is parity in testing bindings across all languages. *) diff --git a/generator/main.ml b/generator/main.ml index 5de89138c..ffe1bb95c 100644 --- a/generator/main.ml +++ b/generator/main.ml @@ -374,7 +374,7 @@ Run it from the top source directory using the command output_to "rust/src/lib.rs" Rust.generate_rust; - output_to "rust/tests/bind_test.rs" + output_to "rust/src/bin/bindtests.rs" Bindtests.generate_rust_bindtests; (* Generate the list of files generated -- last. *) diff --git a/generator/rust.ml b/generator/rust.ml index 4766357be..ee65b1073 100644 --- a/generator/rust.ml +++ b/generator/rust.ml @@ -61,14 +61,16 @@ let generate_rust () pr " use std::collections; use std::convert; +use std::convert::TryFrom; use std::ffi; -use std::slice; -use std::ptr; use std::os::raw::{c_char, c_int, c_void}; +use std::ptr; +use std::slice; +use std::str; #[allow(non_camel_case_types)] -enum guestfs_h {} // opaque struct +enum guestfs_h {} // opaque struct #[link(name = \"guestfs\")] extern \"C\" { @@ -141,22 +143,22 @@ impl CreateFlags { } struct NullTerminatedIter<T: Copy + Clone> { - p: *const T + p: *const *const T, } impl<T: Copy + Clone> NullTerminatedIter<T> { - fn new(p: *const T) -> NullTerminatedIter<T> { - NullTerminatedIter{ p } + fn new(p: *const *const T) -> NullTerminatedIter<T> { + NullTerminatedIter { p } } } impl<T: Copy + Clone> Iterator for NullTerminatedIter<T> { - type Item = T; - fn next(&mut self) -> Option<T> { - if self.p.is_null() { + type Item = *const T; + fn next(&mut self) -> Option<*const T> { + let r = unsafe { *(self.p) }; + if r.is_null() { None } else { - let r = unsafe { *(self.p) }; self.p = unsafe { self.p.offset(1) }; Some(r) } @@ -171,12 +173,15 @@ struct RawList<T> { struct RawListIter<'a, T> { current: u32, - list: &'a RawList<T> + list: &'a RawList<T>, } -impl<T> RawList<T> { +impl<T> RawList<T> { fn iter<'a>(&'a self) -> RawListIter<'a, T> { - RawListIter{ current: 0, list: self } + RawListIter { + current: 0, + list: self, + } } } @@ -193,60 +198,81 @@ impl<'a, T> Iterator for RawListIter<'a, T> { } } - -fn arg_string_list (v: &Vec<&str>) -> Vec<*const i8> { - let length = v.len(); +fn arg_string_list(v: &[&str]) -> Result<Vec<ffi::CString>, Error> { let mut w = Vec::new(); for x in v.iter() { let y: &str = x; - let s = ffi::CString::new(y).unwrap(); - w.push(s.as_ptr()); + w.push(ffi::CString::new(y)?); + } + Ok(w) +} + +fn free_string_list(l: *const *const c_char) { + for buf in NullTerminatedIter::new(l) { + unsafe { free(buf as * const c_void) }; } - w.push(ptr::null()); - w + unsafe { free(l as *const c_void) }; } -fn hashmap (l: *const *const c_char) -> collections::HashMap<String, String> { +fn hashmap(l: *const *const c_char) -> Result<collections::HashMap<String, String>, Error> { let mut map = collections::HashMap::new(); let mut iter = NullTerminatedIter::new(l); while let Some(key) = iter.next() { if let Some(val) = iter.next() { - let key = unsafe { ffi::CStr::from_ptr(key) }.to_str().unwrap(); - let val = unsafe { ffi::CStr::from_ptr(val) }.to_str().unwrap(); + let key = unsafe { ffi::CStr::from_ptr(key) }.to_str()?; + let val = unsafe { ffi::CStr::from_ptr(val) }.to_str()?; map.insert(key.to_string(), val.to_string()); } else { + // Internal Error -> panic panic!(\"odd number of items in hash table\"); } } - map + Ok(map) } -fn struct_list<T, S: convert::From<*const T>>(l: *const RawList<T>) -> Vec<S> { +fn struct_list<T, S: TryFrom<*const T, Error = Error>>( + l: *const RawList<T>, +) -> Result<Vec<S>, Error> { let mut v = Vec::new(); - for x in unsafe {&*l}.iter() { - v.push(S::from(x)); + for x in unsafe { &*l }.iter() { + v.push(S::try_from(x)?); } - v + Ok(v) } -fn string_list (l: *const *const c_char) -> Vec<String> { +fn string_list(l: *const *const c_char) -> Result<Vec<String>, Error> { let mut v = Vec::new(); for x in NullTerminatedIter::new(l) { - let s = unsafe { ffi::CStr::from_ptr(x) }.to_str().unwrap(); + let s = unsafe { ffi::CStr::from_ptr(x) }.to_str()?; v.push(s.to_string()); } - v -} - -fn free_string_list(l: *const *const c_char) { - // TODO + Ok(v) } #[derive(Debug)] -pub struct Error { +pub struct APIError { operation: &'static str, message: String, - errno: i32 + errno: i32, +} + +#[derive(Debug)] +pub enum Error { + API(APIError), + IllegalString(ffi::NulError), + Utf8Error(str::Utf8Error), +} + +impl convert::From<ffi::NulError> for Error { + fn from(error: ffi::NulError) -> Self { + Error::IllegalString(error) + } +} + +impl convert::From<str::Utf8Error> for Error { + fn from(error: str::Utf8Error) -> Self { + Error::Utf8Error(error) + } } impl Handle { @@ -271,13 +297,17 @@ impl Handle { fn get_error_from_handle(&self, operation: &'static str) -> Error { let c_msg = unsafe { guestfs_last_error(self.g) }; let message = unsafe { ffi::CStr::from_ptr(c_msg).to_str().unwrap().to_string() }; - let errno = unsafe { guestfs_last_errno(self.g) } ; - Error { operation, message, errno } + let errno = unsafe { guestfs_last_errno(self.g) }; + Error::API(APIError { + operation, + message, + errno, + }) } } pub struct UUID { - uuid: [u8; 32] + uuid: [u8; 32], } impl UUID { @@ -288,7 +318,6 @@ impl UUID { self.uuid } } - "; List.iter ( fun { s_camel_name = name; s_name = c_name; s_cols = cols } -> @@ -325,22 +354,24 @@ impl UUID { ) cols; pr "}\n"; pr "\n"; - pr "impl convert::From<*const Raw%s> for %s {\n" name name; - pr " fn from(raw: *const Raw%s) -> Self {\n" name; - pr " unsafe { %s {\n" name; + pr "impl TryFrom<*const Raw%s> for %s {\n" name name; + pr " type Error = Error;\n"; + pr " fn try_from(raw: *const Raw%s) -> Result<Self, Self::Error> {\n" name; + pr " Ok(unsafe {\n"; + pr " %s {\n" name; List.iter ( fun x -> - indent 3; + indent 4; match x with | n, FChar -> pr "%s: (*raw).%s as i8,\n" n n; | n, FString -> pr "%s: {\n" n; - indent 4; + indent 5; pr "let s = ffi::CStr::from_ptr((*raw).%s);\n" n; + indent 5; + pr "s.to_str()?.to_string()\n"; indent 4; - pr "s.to_str().unwrap().to_string()\n"; - indent 3; pr "},\n" | n, FBuffer -> pr "%s: slice::from_raw_parts((*raw).%s as *const u8, (*raw).%s_len).to_vec(),\n" n n n @@ -349,9 +380,14 @@ impl UUID { | n, (FUInt32 | FInt32 | FUInt64 | FInt64 | FBytes) -> pr "%s: (*raw).%s,\n" n n | n, FOptPercent -> - pr "%s: if (*raw).%s < 0.0 { None } else { Some((*raw).%s) },\n" n n n + pr "%s: if (*raw).%s < 0.0 {\n" n n; + indent 4; pr " None\n"; + indent 4; pr "} else {\n"; + indent 4; pr " Some((*raw).%s)\n" n; + indent 4; pr"},\n" ) cols; - pr " } }\n"; + pr " }\n"; + pr " })\n"; pr " }\n"; pr "}\n" ) external_structs; @@ -361,11 +397,26 @@ impl UUID { pr "extern \"C\" {\n"; List.iter ( fun { s_camel_name = name; s_name = c_name; } -> - pr "fn guestfs_free_%s(v: *const Raw%s);\n" c_name name; - pr "fn guestfs_free_%s_list(l: *const RawList<Raw%s>);\n" c_name name; + pr " #[allow(dead_code)]\n"; + pr " fn guestfs_free_%s(v: *const Raw%s);\n" c_name name; + pr " #[allow(dead_code)]\n"; + pr " fn guestfs_free_%s_list(l: *const RawList<Raw%s>);\n" c_name name; ) external_structs; pr "}\n"; + (* [Outline] There are three types for each optional structs: SOptArgs, + * CExprSOptArgs, RawSOptArgs. + * SOptArgs: for Rust bindings' API. This can be seen by bindings' users. + * CExprSOptArgs: Each field has C expression(e.g. CString, *const c_char) + * RawSOptArgs: Each field has raw pointers or integer values + * + * SOptArgs ---try_into()---> CExprSOptArgs ---to_raw()---> RawSOptArgs + * + * Note: direct translation from SOptArgs to RawSOptArgs will cause a memory + * management problem. Using into_raw/from_raw, this problem can be avoided, + * but it is complex to handle freeing memories manually in Rust because of + * panic/?/etc. + *) (* generate structs for optional arguments *) List.iter ( @@ -383,27 +434,75 @@ impl UUID { pr "\n"; pr "/* Optional Structs */\n"; pr "#[derive(Default)]\n"; - pr "pub struct OptArgs%s%s {\n" cname opt_life_parameter; + pr "pub struct %sOptArgs%s {\n" cname opt_life_parameter; List.iter ( fun optarg -> let n = translate_bad_symbols (name_of_optargt optarg) in match optarg with | OBool _ -> - pr " _%s: Option<bool>,\n" n + pr " pub %s: Option<bool>,\n" n | OInt _ -> - pr " _%s: Option<i32>,\n" n + pr " pub %s: Option<i32>,\n" n | OInt64 _ -> - pr " _%s: Option<i64>,\n" n + pr " pub %s: Option<i64>,\n" n | OString _ -> - pr " _%s: Option<&'a str>,\n" n + pr " pub %s: Option<&'a str>,\n" n | OStringList _ -> - pr " _%s: Option<Vec<&'a str>>,\n" n + pr " pub %s: Option<&'a [&'a str]>,\n" n ) optargs; pr "}\n\n"; + pr "struct CExpr%sOptArgs {\n" cname; + List.iter ( + fun optarg -> + let n = translate_bad_symbols (name_of_optargt optarg) in + match optarg with + | OBool _ | OInt _ -> + pr " %s: Option<c_int>,\n" n + | OInt64 _ -> + pr " %s: Option<i64>,\n" n + | OString _ -> + pr " %s: Option<ffi::CString>,\n" n + | OStringList _ -> + (* buffers and their pointer vector *) + pr " %s: Option<(Vec<ffi::CString>, Vec<*const c_char>)>,\n" n + ) optargs; + pr "}\n\n"; + + pr "impl%s TryFrom<%sOptArgs%s> for CExpr%sOptArgs {\n" + opt_life_parameter cname opt_life_parameter cname; + pr " type Error = Error;\n"; + pr " fn try_from(optargs: %sOptArgs%s) -> Result<Self, Self::Error> {\n" cname opt_life_parameter; + pr " Ok(CExpr%sOptArgs {\n" cname; + List.iteri ( + fun index optarg -> + let n = translate_bad_symbols (name_of_optargt optarg) in + match optarg with + | OBool _ -> + pr " %s: optargs.%s.map(|b| if b { 1 } else { 0 }),\n" n n; + | OInt _ | OInt64 _ -> + pr " %s: optargs.%s, \n" n n; + | OString _ -> + pr " %s: optargs.%s.map(|v| ffi::CString::new(v)).transpose()?,\n" n n; + | OStringList _ -> + pr " %s: optargs.%s.map(\n" n n; + pr " |v| Ok::<_, Error>({\n"; + pr " let v = arg_string_list(v)?;\n"; + pr " let mut w = (&v).into_iter()\n"; + pr " .map(|v| v.as_ptr())\n"; + pr " .collect::<Vec<_>>();\n"; + pr " w.push(ptr::null());\n"; + pr " (v, w)\n"; + pr " })\n"; + pr " ).transpose()?,\n"; + ) optargs; + pr " })\n"; + pr " }\n"; + pr "}\n"; + (* raw struct for C bindings *) pr "#[repr(C)]\n"; - pr "struct RawOptArgs%s {\n" cname; + pr "struct Raw%sOptArgs {\n" cname; pr " bitmask: u64,\n"; List.iter ( fun optarg -> @@ -422,41 +521,33 @@ impl UUID { ) optargs; pr "}\n\n"; - pr "impl%s convert::From<OptArgs%s%s> for RawOptArgs%s {\n" - opt_life_parameter cname opt_life_parameter cname; - pr " fn from(optargs: OptArgs%s%s) -> Self {\n" cname opt_life_parameter; + pr "impl convert::From<&CExpr%sOptArgs> for Raw%sOptArgs {\n" + cname cname; + pr " fn from(optargs: &CExpr%sOptArgs) -> Self {\n" cname; pr " let mut bitmask = 0;\n"; - pr " RawOptArgs%s {\n" cname; + pr " Raw%sOptArgs {\n" cname; List.iteri ( fun index optarg -> let n = translate_bad_symbols (name_of_optargt optarg) in match optarg with - | OBool _ -> - pr " %s: if let Some(v) = optargs._%s {\n" n n; - pr " bitmask |= 1 << %d;\n" index; - pr " if v { 1 } else { 0 }\n"; - pr " } else {\n"; - pr " 0\n"; - pr " },\n"; - | OInt _ | OInt64 _ -> - pr " %s: if let Some(v) = optargs._%s {\n" n n; + | OBool _ | OInt _ | OInt64 _ -> + pr " %s: if let Some(v) = optargs.%s {\n" n n; pr " bitmask |= 1 << %d;\n" index; pr " v\n"; pr " } else {\n"; pr " 0\n"; pr " },\n"; | OString _ -> - pr " %s: if let Some(v) = optargs._%s {\n" n n; + pr " %s: if let Some(ref v) = optargs.%s {\n" n n; pr " bitmask |= 1 << %d;\n" index; - pr " let y: &str = &v;\n"; - pr " ffi::CString::new(y).unwrap().as_ptr()\n"; + pr " v.as_ptr()\n"; pr " } else {\n"; pr " ptr::null()\n"; pr " },\n"; | OStringList _ -> - pr " %s: if let Some(v) = optargs._%s {\n" n n; + pr " %s: if let Some((_, ref v)) = optargs.%s {\n" n n; pr " bitmask |= 1 << %d;\n" index; - pr " arg_string_list(&v).as_ptr()"; + pr " v.as_ptr()\n"; pr " } else {\n"; pr " ptr::null()\n"; pr " },\n"; @@ -465,29 +556,6 @@ impl UUID { pr " }\n"; pr " }\n"; pr "}\n"; - - pr "impl%s OptArgs%s%s {\n" opt_life_parameter cname opt_life_parameter; - List.iter ( - fun optarg -> - let n = translate_bad_symbols (name_of_optargt optarg) in - pr " pub fn %s(self, %s: " n n; - (match optarg with - | OBool _ -> - pr "bool" - | OInt _ -> - pr "i32" - | OInt64 _ -> - pr "i64" - | OString _ -> - pr "&'a str" - | OStringList _ -> - pr "Vec<&'a str>" - ); - pr ") -> OptArgs%s%s {\n" cname opt_life_parameter; - pr " OptArgs%s { _%s: Some(%s), ..self }\n" cname n n; - pr " }\n" - ) optargs; - pr "}\n\n"; ); ) (actions |> external_functions |> sort); @@ -497,7 +565,8 @@ impl UUID { fun ({ name = name; shortdesc = shortdesc; style = (ret, args, optargs) } as f) -> let cname = snake2caml name in - pr "fn %s(g: *const guestfs_h" f.c_function; + pr " #[allow(non_snake_case)]\n"; + pr " fn %s(g: *const guestfs_h" f.c_function; List.iter ( fun arg -> pr ", "; @@ -516,7 +585,7 @@ impl UUID { | _ -> () ); if optargs <> [] then - pr ", optarg: *const RawOptArgs%s" cname; + pr ", optarg: *const Raw%sOptArgs" cname; pr ") -> "; @@ -541,14 +610,16 @@ impl UUID { pr "impl Handle {\n"; List.iter ( - fun ({ name = name; shortdesc = shortdesc; + fun ({ name = name; shortdesc = shortdesc; longdesc = longdesc; style = (ret, args, optargs) } as f) -> let cname = snake2caml name in - pr " /// %s \n" shortdesc; + pr " /// %s\n" shortdesc; + pr " #[allow(non_snake_case)]\n"; pr " pub fn %s" name; (* generate arguments *) pr "(&self, "; + let comma = ref false in List.iter ( fun arg -> @@ -560,14 +631,14 @@ impl UUID { | Int64 n -> pr "%s: i64" n | String (_, n) -> pr "%s: &str" n | OptString n -> pr "%s: Option<&str>" n - | StringList (_, n) -> pr "%s: Vec<&str>" n - | BufferIn n -> pr "%s: Vec<u8>" n - | Pointer (_, n) -> pr "%s: usize" n + | StringList (_, n) -> pr "%s: &[&str]" n + | BufferIn n -> pr "%s: &[u8]" n + | Pointer (_, n) -> pr "%s: *mut c_void" n ) args; if optargs <> [] then ( if !comma then pr ", "; comma := true; - pr "optargs: OptArgs%s" cname + pr "optargs: %sOptArgs" cname ); pr ")"; @@ -592,45 +663,37 @@ impl UUID { | RBufferOut _ -> pr "Vec<u8>"); pr ", Error> {\n"; + let _pr = pr in let pr fs = indent 2; pr fs in List.iter ( function | Bool n -> - pr "let c_%s = if %s { 1 } else { 0 };\n" n n + pr "let %s = if %s { 1 } else { 0 };\n" n n | String (_, n) -> - (* TODO: handle errors *) - pr "let c_%s = \n" n; - pr " ffi::CString::new(%s).expect(\"CString::new failed\").as_ptr();\n" n; + pr "let c_%s = ffi::CString::new(%s)?;\n" n n; | OptString n -> - pr "let c_%s = match %s {" n n; - pr " Some(s) => \n"; - pr " ffi::CString::new(s)\n"; - pr " .expect(\"CString::new failed\")\n"; - pr " .as_ptr(),\n"; - pr " None => ptr::null(),\n"; - pr "};\n"; + pr "let c_%s = %s.map(|s| ffi::CString::new(s)).transpose()?;\n" n n; | StringList (_, n) -> - pr "let c_%s = arg_string_list(&%s).as_ptr();\n" n n; + pr "let c_%s_v = arg_string_list(%s)?;\n" n n; + pr "let mut c_%s = (&c_%s_v).into_iter().map(|v| v.as_ptr()).collect::<Vec<_>>();\n" n n; + pr "c_%s.push(ptr::null());\n" n; | BufferIn n -> - pr "let c_%s_len = (&%s).len();\n" n n; - pr "let c_%s = ffi::CString::new(%s)\n" n n; - pr " .expect(\"CString::new failed\")\n"; - pr " .as_ptr();\n"; + pr "let c_%s_len = %s.len();\n" n n; + pr "let c_%s = unsafe { ffi::CString::from_vec_unchecked(%s.to_vec())};\n" n n; | Int _ | Int64 _ | Pointer _ -> () ) args; - (* TODO: handle optargs *) - if optargs <> [] then ( - pr "let optargs_raw = RawOptArgs%s::from(optargs);\n" cname; - ); - (match ret with | RBufferOut _ -> pr "let mut size = 0usize;\n" | _ -> () ); + if optargs <> [] then ( + pr "let optargs_cexpr = CExpr%sOptArgs::try_from(optargs)?;\n" cname; + ); + pr "\n"; pr "let r = unsafe { %s(self.g" f.c_function; @@ -639,18 +702,19 @@ impl UUID { fun arg -> pr ", "; match arg with - | Bool n | String (_, n) | OptString n -> pr "c_%s" n - | Int n | Int64 n -> pr "%s" n - | Pointer _ -> pr "ptr::null()" (* XXX: what is pointer? *) - | StringList (_, n) -> pr "c_%s as *const *const c_char" n - | BufferIn n -> pr "c_%s, c_%s_len" n n + | String (_, n) -> pr "(&c_%s).as_ptr()" n + | OptString n -> pr "match &c_%s { Some(ref s) => s.as_ptr(), None => ptr::null() }\n" n + | StringList (_, n) -> pr "(&c_%s).as_ptr() as *const *const c_char" n + | Bool n | Int n | Int64 n | Pointer (_, n) -> pr "%s" n + | BufferIn n -> pr "(&c_%s).as_ptr(), c_%s_len" n n ) args; (match ret with - | RBufferOut _ -> pr ", &size as *const usize" + | RBufferOut _ -> pr ", &mut size as *mut usize" | _ -> () ); if optargs <> [] then ( - pr ", &optargs_raw as *const RawOptArgs%s" cname; + pr ", &(Raw%sOptArgs::from(&optargs_cexpr)) as *const Raw%sOptArgs" + cname cname; ); pr ") };\n"; @@ -660,66 +724,85 @@ impl UUID { | `CannotReturnError -> () | `ErrorIsMinusOne -> pr "if r == -1 {\n"; - pr " return Err(self.get_error_from_handle (\"%s\"));\n" name; + pr " return Err(self.get_error_from_handle(\"%s\"));\n" name; pr "}\n" | `ErrorIsNULL -> pr "if r.is_null() {\n"; - pr " return Err(self.get_error_from_handle (\"%s\"));\n" name; + pr " return Err(self.get_error_from_handle(\"%s\"));\n" name; pr "}\n" ); + + (* This part is not required, but type system will guarantee that + * the buffers are still alive. This is useful because Rust cannot + * know whether raw pointers used above are alive or not. + *) + List.iter ( + function + | Bool _ | Int _ | Int64 _ | Pointer _ -> () + | String (_, n) + | OptString n + | BufferIn n -> pr "drop(c_%s);\n" n; + | StringList (_, n) -> + pr "drop(c_%s);\n" n; + pr "drop(c_%s_v);\n" n; + ) args; + if optargs <> [] then ( + pr "drop(optargs_cexpr);\n"; + ); + pr "Ok("; let pr = _pr in + let pr3 fs = indent 3; pr fs in (match ret with | RErr -> pr "()" | RInt _ | RInt64 _ -> pr "r" | RBool _ -> pr "r != 0" | RConstString _ -> - pr "unsafe{ ffi::CStr::from_ptr(r) }.to_str().unwrap()\n" + pr "unsafe{ ffi::CStr::from_ptr(r) }.to_str()?" | RString _ -> - pr "{"; - pr " let s = unsafe {ffi::CStr::from_ptr(r)}\n"; - pr " .to_str().unwrap().to_string();\n"; - pr " unsafe { free(r as * const c_void) };\n"; - pr " s\n"; - pr "}\n"; + pr "{\n"; + pr3 "let s = unsafe { ffi::CStr::from_ptr(r) };\n"; + pr3 "unsafe { free(r as *const c_void) };\n"; + pr3 "s.to_str()?.to_string()\n"; + indent 2; pr "}"; | RConstOptString _ -> - indent 3; pr "if r.is_null() {\n"; - indent 3; pr " None\n"; - indent 3; pr "} else {\n"; - indent 3; pr " Some(unsafe { ffi::CStr::from_ptr(r) }.to_str().unwrap())\n"; - indent 3; pr "}"; + pr "if r.is_null() {\n"; + pr3 "None\n"; + indent 2; pr "} else {\n"; + pr3 "Some(unsafe { ffi::CStr::from_ptr(r) }.to_str()?)\n"; + indent 2; pr "}"; | RStringList _ -> pr "{\n"; - pr " let s = string_list(r);\n"; - pr " free_string_list(r);\n"; - pr " s\n"; - pr "}\n"; + pr3 "let s = string_list(r);\n"; + pr3 "free_string_list(r);\n"; + pr3 "s?\n"; + indent 2; pr "}"; | RStruct (_, n) -> let sn = camel_name_of_struct n in pr "{\n"; - pr " let s = %s::from(r);\n" sn; - pr " unsafe { guestfs_free_%s(r) };\n" n; - pr " s\n"; - pr "}\n"; + pr3 "let s = %s::try_from(r);\n" sn; + pr3 "unsafe { guestfs_free_%s(r) };\n" n; + pr3 "s?\n"; + indent 2; pr "}"; | RStructList (_, n) -> let sn = camel_name_of_struct n in pr "{\n"; - pr " let l = struct_list::<Raw%s, %s>(r);\n" sn sn; - pr " unsafe { guestfs_free_%s_list(r) };" n; - pr " l\n"; - pr "}\n"; + pr3 "let l = struct_list::<Raw%s, %s>(r);\n" sn sn; + pr3 "unsafe { guestfs_free_%s_list(r) };\n" n; + pr3 "l?\n"; + indent 2; pr "}"; | RHashtable _ -> pr "{\n"; - pr " let h = hashmap(r);\n"; - pr " free_string_list(r);\n"; - pr " h\n"; - pr "}\n"; + pr3 "let h = hashmap(r);\n"; + pr3 "free_string_list(r);\n"; + pr3 "h?\n"; + indent 2; pr "}"; | RBufferOut _ -> pr "{\n"; - pr " let s = unsafe { slice::from_raw_parts(r, size) }.to_vec();\n"; - pr " unsafe { free(r as * const c_void) } ;\n"; - pr " s\n"; - pr "}\n"; + pr3 "let s = unsafe { slice::from_raw_parts(r, size) }.to_vec();\n"; + pr3 "unsafe { free(r as *const c_void) } ;\n"; + pr3 "s\n"; + indent 2; pr "}"; ); pr ")\n"; pr " }\n\n" diff --git a/generator/rust.mli b/generator/rust.mli index 4fef55d4e..5410286c8 100644 --- a/generator/rust.mli +++ b/generator/rust.mli @@ -16,4 +16,7 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA *) -val generate_rust: unit -> unit \ No newline at end of file +val generate_rust: unit -> unit + +(* for bindtests.ml *) +val snake2caml: string -> string diff --git a/rust/.gitignore b/rust/.gitignore new file mode 100644 index 000000000..53eaa2196 --- /dev/null +++ b/rust/.gitignore @@ -0,0 +1,2 @@ +/target +**/*.rs.bk diff --git a/rust/Makefile.am b/rust/Makefile.am index e8bf27894..261cf4a5c 100644 --- a/rust/Makefile.am +++ b/rust/Makefile.am @@ -18,12 +18,26 @@ include $(top_srcdir)/subdir-rules.mk generator_built = \ + src/bin/bindtests.rs \ src/lib.rs EXTRA_DIST = \ - $(generator_built) + .gitignore \ + $(generator_built) \ + tests/*.rs \ + Cargo.toml \ + Cargo.lock \ + run-bindtests \ + run-tests if HAVE_RUST +all: src/lib.rs + $(top_builddir)/run $(CARGO) build --release + +TESTS = run-bindtests run-tests + +CLEANFILES += target/*~ + endif diff --git a/rust/run-bindtests b/rust/run-bindtests new file mode 100755 index 000000000..2986e898d --- /dev/null +++ b/rust/run-bindtests @@ -0,0 +1,23 @@ +#!/bin/sh - +# libguestfs Golang bindings +# Copyright (C) 2013 Red Hat Inc. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +set -e + +$CARGO run --bin bindtests > bindtests.tmp +diff -u $srcdir/../bindtests bindtests.tmp +rm bindtests.tmp diff --git a/rust/run-tests b/rust/run-tests new file mode 100755 index 000000000..694ecc9dd --- /dev/null +++ b/rust/run-tests @@ -0,0 +1,21 @@ +#!/bin/sh - +# libguestfs Golang tests +# Copyright (C) 2013 Red Hat Inc. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +set -e + +$CARGO test diff --git a/rust/src/bin/.gitkeep b/rust/src/bin/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/rust/tests/010_load.rs b/rust/tests/010_load.rs index eadd78896..4cb43f2c1 100644 --- a/rust/tests/010_load.rs +++ b/rust/tests/010_load.rs @@ -1,20 +1,20 @@ -/* libguestfs Python bindings - Copyright (C) 2009-2019 Red Hat Inc. +/* libguestfs Rust bindings +Copyright (C) 2009-2019 Red Hat Inc. - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ extern crate guestfs; diff --git a/rust/tests/020_create.rs b/rust/tests/020_create.rs index 0b57b19d7..017dbbac0 100644 --- a/rust/tests/020_create.rs +++ b/rust/tests/020_create.rs @@ -1,20 +1,20 @@ -/* libguestfs Python bindings - Copyright (C) 2009-2019 Red Hat Inc. +/* libguestfs Rust bindings +Copyright (C) 2009-2019 Red Hat Inc. - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ extern crate guestfs; diff --git a/rust/tests/030_create_flags.rs b/rust/tests/030_create_flags.rs index 5de0589c1..df3190d4c 100644 --- a/rust/tests/030_create_flags.rs +++ b/rust/tests/030_create_flags.rs @@ -1,19 +1,19 @@ -/* libguestfs Python bindings - Copyright (C) 2009-2019 Red Hat Inc. +/* libguestfs Rust bindings +Copyright (C) 2009-2019 Red Hat Inc. - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ extern crate guestfs; @@ -23,8 +23,7 @@ use guestfs::*; fn create_flags() { let _h = Handle::create_flags(CreateFlags::none()).expect("create_flags fail"); // TODO: Add parse_environment to check the flag is created correctly - let flags = CreateFlags::new() - .create_no_environment(true); + let flags = CreateFlags::new().create_no_environment(true); let _h = Handle::create_flags(flags).expect("create_flags fail"); // TODO: Add parse_environment to check the flag is created correctly } diff --git a/rust/tests/040_create_multiple.rs b/rust/tests/040_create_multiple.rs index ee481c278..372fad7ee 100644 --- a/rust/tests/040_create_multiple.rs +++ b/rust/tests/040_create_multiple.rs @@ -1,20 +1,20 @@ -/* libguestfs Python bindings - Copyright (C) 2009-2019 Red Hat Inc. +/* libguestfs Rust bindings +Copyright (C) 2009-2019 Red Hat Inc. - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ extern crate guestfs; diff --git a/rust/tests/050_handle_properties.rs b/rust/tests/050_handle_properties.rs index 3c7b449f9..0b955d5cf 100644 --- a/rust/tests/050_handle_properties.rs +++ b/rust/tests/050_handle_properties.rs @@ -1,20 +1,20 @@ -/* libguestfs Python bindings - Copyright (C) 2009-2019 Red Hat Inc. +/* libguestfs Rust bindings +Copyright (C) 2009-2019 Red Hat Inc. - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ extern crate guestfs; @@ -57,5 +57,6 @@ fn path() { #[test] fn add_drive() { let g = guestfs::Handle::create().expect("create"); - g.add_drive("/dev/null", Default::default()).expect("add_drive"); + g.add_drive("/dev/null", Default::default()) + .expect("add_drive"); } diff --git a/rust/tests/070_opt_args.rs b/rust/tests/070_opt_args.rs index 241c95b35..04b4890c2 100644 --- a/rust/tests/070_opt_args.rs +++ b/rust/tests/070_opt_args.rs @@ -1,20 +1,20 @@ -/* libguestfs Python bindings - Copyright (C) 2009-2019 Red Hat Inc. +/* libguestfs Rust bindings +Copyright (C) 2009-2019 Red Hat Inc. - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ extern crate guestfs; @@ -30,7 +30,12 @@ fn no_optargs() { #[test] fn one_optarg() { let g = guestfs::Handle::create().expect("create"); - g.add_drive("/dev/null", - guestfs::OptArgsAddDrive::default().readonly(true)) - .expect("add_drive"); + g.add_drive( + "/dev/null", + guestfs::AddDriveOptArgs { + readonly: Some(true), + ..Default::default() + }, + ) + .expect("add_drive"); } diff --git a/rust/tests/080_version.rs b/rust/tests/080_version.rs index ab5314edc..19e441d67 100644 --- a/rust/tests/080_version.rs +++ b/rust/tests/080_version.rs @@ -1,29 +1,26 @@ -/* libguestfs Python bindings - Copyright (C) 2009-2019 Red Hat Inc. +/* libguestfs Rust bindings +Copyright (C) 2009-2019 Red Hat Inc. - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ extern crate guestfs; -use std::default::Default; - #[test] fn version() { let g = guestfs::Handle::create().expect("create"); let v = g.version().expect("version"); assert_eq!(v.major, 1) } - diff --git a/rust/tests/090_ret_values.rs b/rust/tests/090_ret_values.rs new file mode 100644 index 000000000..d3e2e80da --- /dev/null +++ b/rust/tests/090_ret_values.rs @@ -0,0 +1,61 @@ +/* libguestfs Rust bindings +Copyright (C) 2009-2019 Red Hat Inc. + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +extern crate guestfs; + +#[test] +fn rint() { + let g = guestfs::Handle::create().expect("create"); + assert_eq!(g.internal_test_rint("10").unwrap(), 10); + assert!(g.internal_test_rinterr().is_err()) +} + +#[test] +fn rint64() { + let g = guestfs::Handle::create().expect("create"); + assert_eq!(g.internal_test_rint64("10").unwrap(), 10); + assert!(g.internal_test_rint64err().is_err()) +} + +#[test] +fn rbool() { + let g = guestfs::Handle::create().expect("create"); + assert!(g.internal_test_rbool("true").unwrap()); + assert!(!g.internal_test_rbool("false").unwrap()); + assert!(g.internal_test_rboolerr().is_err()) +} + +#[test] +fn rconststring() { + let g = guestfs::Handle::create().expect("create"); + assert_eq!( + g.internal_test_rconststring("test").unwrap(), + "static string" + ); + assert!(g.internal_test_rconststringerr().is_err()) +} + +#[test] +fn rconstoptstring() { + let g = guestfs::Handle::create().expect("create"); + assert_eq!( + g.internal_test_rconstoptstring("test").unwrap(), + Some("static string") + ); + assert_eq!(g.internal_test_rconstoptstringerr().unwrap(), None) +} diff --git a/rust/tests/100_launch.rs b/rust/tests/100_launch.rs new file mode 100644 index 000000000..1c1d8146a --- /dev/null +++ b/rust/tests/100_launch.rs @@ -0,0 +1,65 @@ +/* libguestfs Rust bindings +Copyright (C) 2009-2019 Red Hat Inc. + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +extern crate guestfs; + +use std::default::Default; + +#[test] +fn launch() { + let g = guestfs::Handle::create().expect("create"); + g.add_drive_scratch(500 * 1024 * 1024, Default::default()) + .expect("add_drive_scratch"); + g.launch().expect("launch"); + g.pvcreate("/dev/sda").expect("pvcreate"); + g.vgcreate("VG", &["/dev/sda"]).expect("vgcreate"); + g.lvcreate("LV1", "VG", 200).expect("lvcreate"); + g.lvcreate("LV2", "VG", 200).expect("lvcreate"); + + let lvs = g.lvs().expect("lvs"); + assert_eq!( + lvs, + vec!["/dev/VG/LV1".to_string(), "/dev/VG/LV2".to_string()] + ); + + g.mkfs("ext2", "/dev/VG/LV1", Default::default()) + .expect("mkfs"); + g.mount("/dev/VG/LV1", "/").expect("mount"); + g.mkdir("/p").expect("mkdir"); + g.touch("/q").expect("touch"); + + let mut dirs = g.readdir("/").expect("readdir"); + + dirs.sort_by(|a, b| a.name.cmp(&b.name)); + + let mut v = Vec::new(); + for x in &dirs { + v.push((x.name.as_str(), x.ftyp as u8)); + } + assert_eq!( + v, + vec![ + (".", b'd'), + ("..", b'd'), + ("lost+found", b'd'), + ("p", b'd'), + ("q", b'r') + ] + ); + g.shutdown().expect("shutdown"); +} -- 2.20.1 (Apple Git-117)
Richard W.M. Jones
2019-Jun-27 08:46 UTC
Re: [Libguestfs] [PATCH 1/9] Rust bindings: Add Rust bindings
On Thu, Jun 27, 2019 at 05:06:02PM +0900, Hiroyuki Katsura wrote:> diff --git a/generator/bindtests.ml b/generator/bindtests.ml > index 58d7897b3..41aef47ea 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;There is an extra ‘;’ here which is wrong and I'm a bit surprised that it doesn't confuse the compiler too. Just delete the semicolon.> diff --git a/generator/rust.ml b/generator/rust.ml > new file mode 100644 > index 000000000..83afdfe73 > --- /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 > + > +Extra blank line above - please remove it.> +let generate_rust () > + generate_header CStyle LGPLv2plus; > \ No newline at end of fileThe extra semicolon here could be dropped, and you could add a newline at the end of the file too. --- Apart from those things, this patch is fine. I would ACK it if you fixed these. 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
Richard W.M. Jones
2019-Jun-27 08:48 UTC
Re: [Libguestfs] [PATCH 4/9] Rust bindings: Add generator of structs
On Thu, Jun 27, 2019 at 05:06:05PM +0900, Hiroyuki Katsura wrote:> 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 251eb1594..174f6ded3 100644 > --- a/generator/rust.ml > +++ b/generator/rust.ml > @@ -29,13 +29,21 @@ 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; > + > +Extra blank line here.> #[allow(non_camel_case_types)] > -enum guestfs_h {} > +enum guestfs_h {} // opaque struct > > #[link(name = \"guestfs\")] > extern \"C\" { > @@ -119,5 +127,80 @@ impl Handle { > Ok(Handle { g }) > } > } > +}\Did you mean to put a \ at the end of this line? It causes the OCaml compiler to ignore the following newline, which is probably the wrong thing. This patch is fine apart from where noted above. Rich. -- Richard Jones, Virtualization Group, Red Hat http://people.redhat.com/~rjones Read my programming and virtualization blog: http://rwmj.wordpress.com virt-df lists disk usage of guests without needing to install any software inside the virtual machine. Supports Linux and Windows. http://people.redhat.com/~rjones/virt-df/
Richard W.M. Jones
2019-Jun-27 08:49 UTC
Re: [Libguestfs] [PATCH 2/9] Rust bindings: Add create / close functions
Patch 2 is fine, ACK. 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
Richard W.M. Jones
2019-Jun-27 08:52 UTC
Re: [Libguestfs] [PATCH 3/9] Rust bindings: Add 4 bindings tests
On Thu, Jun 27, 2019 at 05:06:04PM +0900, Hiroyuki Katsura wrote:> From: Hiroyuki_Katsura <hiroyuki.katsura.0513@gmail.com> > > --- > generator/rust.ml | 13 ++++++++--- > run.in | 9 ++++++++ > rust/Cargo.lock | 6 +++++ > rust/Cargo.toml | 4 +--- > 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 +++++++++++++++++++++++++++++++ > 8 files changed, 142 insertions(+), 6 deletions(-) > create mode 100644 rust/Cargo.lock > 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/generator/rust.ml b/generator/rust.ml > index dbe9db010..251eb1594 100644 > --- a/generator/rust.ml > +++ b/generator/rust.ml > @@ -37,14 +37,16 @@ let generate_rust () > #[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); > - static GUESTFS_CREATE_NO_ENVIRONMENT: i64; > - static GUESTFS_CREATE_NO_CLOSE_ON_EXIT: i64; > } > > +const GUESTFS_CREATE_NO_ENVIRONMENT: i64 = 1; > +const GUESTFS_CREATE_NO_CLOSE_ON_EXIT: i64 = 2; > + > pub struct Handle { > g: *mut guestfs_h, > } > @@ -61,13 +63,17 @@ pub struct CreateFlags { > } > > impl CreateFlags { > - pub fn new() -> 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; > selfShouldn't these changes be folded into patch 2?> @@ -97,6 +103,7 @@ impl CreateFlags { > impl Handle { > pub fn create() -> Result<Handle, &'static str> { > let g = unsafe { guestfs_create() }; > + println!(\"hoge\");I guess this is a stray debug message?> if g.is_null() { > Err(\"failed to create guestfs handle\") > } else { > 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.lock b/rust/Cargo.lock > new file mode 100644 > index 000000000..c03586e3f > --- /dev/null > +++ b/rust/Cargo.lock > @@ -0,0 +1,6 @@ > +# This file is automatically @generated by Cargo. > +# It is not intended for manual editing. > +[[package]] > +name = "guestfs" > +version = "0.1.0" > + > diff --git a/rust/Cargo.toml b/rust/Cargo.toml > index 6cd94ce6a..4ea7c299b 100644 > --- a/rust/Cargo.toml > +++ b/rust/Cargo.toml > @@ -1,8 +1,6 @@ > [package] > -name = "rust" > +name = "guestfs" > version = "0.1.0" > edition = "2018" > > [dependencies] > -libc = "0.2" > -In nbdkit we added Cargo.lock and Cargo.toml to .gitignore and we did *not* check them into git.> 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)The actual test parts of this patch are fine, but you need to spend a bit of time with ‘git rebase -i’ to move parts of patch 3 into patch 2. Rich. -- Richard Jones, Virtualization Group, Red Hat http://people.redhat.com/~rjones Read my programming and virtualization blog: http://rwmj.wordpress.com virt-df lists disk usage of guests without needing to install any software inside the virtual machine. Supports Linux and Windows. http://people.redhat.com/~rjones/virt-df/
Richard W.M. Jones
2019-Jun-27 08:55 UTC
Re: [Libguestfs] [PATCH 5/9] Rust bindings: Add generator of structs for optional arguments
On Thu, Jun 27, 2019 at 05:06:06PM +0900, Hiroyuki Katsura wrote:> 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 174f6ded3..a229d5eac 100644 > --- a/generator/rust.ml > +++ b/generator/rust.ml > @@ -29,10 +29,32 @@ 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) > | _ -> ()generator/bindtests.ml has a function called ‘pr_indent’ which is a bit like this. Moving it to generator/utils.ml might be worth considering, then you can use it from both places.> +(* 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 > + > +Extra blank line.> +(* 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; > > @@ -204,3 +226,52 @@ impl UUID { > pr " }\n"; > 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)The rest of this patch looks OK. Rich. -- Richard Jones, Virtualization Group, Red Hat http://people.redhat.com/~rjones Read my programming and virtualization blog: http://rwmj.wordpress.com Fedora Windows cross-compiler. Compile Windows programs, test, and build Windows installers. Over 100 libraries supported. http://fedoraproject.org/wiki/MinGW
Richard W.M. Jones
2019-Jun-27 08:58 UTC
Re: [Libguestfs] [PATCH 6/9] Rust bindings: Add generator of function signatures
On Thu, Jun 27, 2019 at 05:06:07PM +0900, Hiroyuki Katsura wrote:> From: Hiroyuki_Katsura <hiroyuki.katsura.0513@gmail.com> > > --- > generator/rust.ml | 137 ++++++++++++++++++++++++++++++++++++++++++++++ > 1 file changed, 137 insertions(+) > > diff --git a/generator/rust.ml b/generator/rust.ml > index a229d5eac..aa8b249ff 100644 > --- a/generator/rust.ml > +++ b/generator/rust.ml > @@ -59,8 +59,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; > > > @@ -130,6 +132,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() }; > @@ -151,6 +165,10 @@ impl Handle { > } > }\ > > +pub struct Error { > + // TODO > +} > + > pub struct UUID { > uuid: [u8; 32] > } > @@ -226,6 +244,8 @@ impl UUID { > pr " }\n"; > pr "}\n" > ) external_structs; > + > + (* generate structs for optional arguments *) > List.iter ( > fun ({ name = name; shortdesc = shortdesc; > style = (ret, args, optargs) }) -> > @@ -275,3 +295,120 @@ impl UUID { > pr "}\n\n"; > ); > ) (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? *)It's used only by one libguestfs function (guestfs_add_domain), and it requires you to look at the rust libvirt bindings to find out how to implement it. It's safe to ignore it for now.> + | 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)This patch looks OK. Rich. -- Richard Jones, Virtualization Group, Red Hat http://people.redhat.com/~rjones Read my programming and virtualization blog: http://rwmj.wordpress.com virt-top is 'top' for virtual machines. Tiny program with many powerful monitoring features, net stats, disk stats, logging, etc. http://people.redhat.com/~rjones/virt-top
Richard W.M. Jones
2019-Jun-27 08:59 UTC
Re: [Libguestfs] [PATCH 7/9] Rust bindings: Complete actions
This one looks OK. 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
Richard W.M. Jones
2019-Jun-27 09:01 UTC
Re: [Libguestfs] [PATCH 9/9] Rust bindings: Complete bindings
Patch 9 is a kind of dumping ground of all kinds of stuff. It may be better to spend some time with git rebase -i trying to work this into more coherent patches. Rich. -- Richard Jones, Virtualization Group, Red Hat http://people.redhat.com/~rjones Read my programming and virtualization blog: http://rwmj.wordpress.com virt-p2v converts physical machines to virtual machines. Boot with a live CD or over the network (PXE) and turn machines into KVM guests. http://libguestfs.org/virt-v2v