[Libguestfs] [PATCH] Rust bindings: Add Rust bindings
Hiroyuki Katsura
hiroyuki.katsura.0513 at gmail.com
Tue Jul 23 08:36:23 UTC 2019
I found a bug in the bindings, so I fixed it. I'm sorry about sending you
the patch many times.
Regards,
Hiroyuki
2019年7月20日(土) 16:23 Hiroyuki Katsura <hiroyuki.katsura.0513 at gmail.com>:
> From: Hiroyuki_Katsura <hiroyuki.katsura.0513 at gmail.com>
>
> Rust bindings: Add create / close functions
>
> Rust bindings: Add 4 bindings tests
>
> Rust bindings: Add generator of structs
>
> Rust bindings: Add generator of structs for optional arguments
>
> Rust bindings: Add generator of function signatures
>
> Rust bindings: Complete actions
>
> Rust bindings: Fix memory management
>
> Rust bindings: Add bindtests
>
> Rust bindings: Add additional 4 bindings tests
>
> Rust bindings: Format test files
>
> Rust bindings: Incorporate bindings to build system
> ---
> Makefile.am | 3 +
> configure.ac | 6 +
> generator/Makefile.am | 3 +
> generator/bindtests.ml | 66 ++++
> generator/bindtests.mli | 1 +
> generator/main.ml | 5 +
> generator/rust.ml | 564 ++++++++++++++++++++++++++++
> generator/rust.mli | 22 ++
> m4/guestfs-rust.m4 | 33 ++
> run.in | 9 +
> rust/.gitignore | 3 +
> rust/Cargo.toml.in | 6 +
> rust/Makefile.am | 42 +++
> rust/run-bindtests | 23 ++
> rust/run-tests | 21 ++
> rust/src/.gitkeep | 0
> rust/src/base.rs | 125 ++++++
> rust/src/bin/.gitkeep | 0
> rust/src/error.rs | 68 ++++
> rust/src/lib.rs | 7 +
> rust/src/utils.rs | 136 +++++++
> rust/tests/.gitkeep | 0
> rust/tests/010_load.rs | 24 ++
> rust/tests/020_create.rs | 24 ++
> rust/tests/030_create_flags.rs | 29 ++
> rust/tests/040_create_multiple.rs | 38 ++
> rust/tests/050_handle_properties.rs | 62 +++
> rust/tests/070_opt_args.rs | 41 ++
> rust/tests/080_version.rs | 26 ++
> rust/tests/090_ret_values.rs | 61 +++
> rust/tests/100_launch.rs | 65 ++++
> 31 files changed, 1513 insertions(+)
> create mode 100644 generator/rust.ml
> create mode 100644 generator/rust.mli
> create mode 100644 m4/guestfs-rust.m4
> create mode 100644 rust/.gitignore
> create mode 100644 rust/Cargo.toml.in
> create mode 100644 rust/Makefile.am
> create mode 100755 rust/run-bindtests
> create mode 100755 rust/run-tests
> create mode 100644 rust/src/.gitkeep
> create mode 100644 rust/src/base.rs
> create mode 100644 rust/src/bin/.gitkeep
> create mode 100644 rust/src/error.rs
> create mode 100644 rust/src/lib.rs
> create mode 100644 rust/src/utils.rs
> create mode 100644 rust/tests/.gitkeep
> create mode 100644 rust/tests/010_load.rs
> create mode 100644 rust/tests/020_create.rs
> create mode 100644 rust/tests/030_create_flags.rs
> create mode 100644 rust/tests/040_create_multiple.rs
> create mode 100644 rust/tests/050_handle_properties.rs
> create mode 100644 rust/tests/070_opt_args.rs
> create mode 100644 rust/tests/080_version.rs
> create mode 100644 rust/tests/090_ret_values.rs
> create mode 100644 rust/tests/100_launch.rs
>
> diff --git a/Makefile.am b/Makefile.am
> index e76ea6daf..c1690386d 100644
> --- a/Makefile.am
> +++ b/Makefile.am
> @@ -151,6 +151,9 @@ endif
> if HAVE_GOLANG
> SUBDIRS += golang golang/examples
> endif
> +if HAVE_RUST
> +SUBDIRS += rust
> +endif
>
> # Unconditional because nothing is built yet.
> SUBDIRS += csharp
> diff --git a/configure.ac b/configure.ac
> index 46bb7684a..4b445953d 100644
> --- a/configure.ac
> +++ b/configure.ac
> @@ -161,6 +161,8 @@ HEADING([Checking for Go])
> m4_include([m4/guestfs-golang.m4])
> HEADING([Checking for GObject Introspection])
> m4_include([m4/guestfs-gobject.m4])
> +HEADING([Checking for Rust])
> +m4_include([m4/guestfs-rust.m4])
> HEADING([Checking for Vala])
> VAPIGEN_CHECK
>
> @@ -315,6 +317,8 @@ AC_CONFIG_FILES([Makefile
> ruby/Rakefile
> ruby/examples/Makefile
> ruby/ext/guestfs/extconf.rb
> + rust/Makefile
> + rust/Cargo.toml
> sparsify/Makefile
> sysprep/Makefile
> test-data/Makefile
> @@ -433,6 +437,8 @@ AS_ECHO_N(["Vala bindings ....................... "])
> if test "x$ENABLE_VAPIGEN_TRUE" = "x"; then echo "yes"; else echo "no"; fi
> AS_ECHO_N(["bash completion ..................... "])
> if test "x$HAVE_BASH_COMPLETION_TRUE" = "x"; then echo "yes"; else echo
> "no"; fi
> +AS_ECHO_N(["Rust bindings ....................... "])
> +if test "x$HAVE_RUST_TRUE" = "x"; then echo "yes"; else echo "no"; fi
> echo
> echo "If any optional component is configured 'no' when you expected
> 'yes'"
> echo "then you should check the preceding messages."
> diff --git a/generator/Makefile.am b/generator/Makefile.am
> index 0322a7561..283cf3769 100644
> --- a/generator/Makefile.am
> +++ b/generator/Makefile.am
> @@ -103,6 +103,8 @@ sources = \
> python.mli \
> ruby.ml \
> ruby.mli \
> + rust.ml \
> + rust.mli \
> structs.ml \
> structs.mli \
> tests_c_api.ml \
> @@ -161,6 +163,7 @@ objects = \
> lua.cmo \
> GObject.cmo \
> golang.cmo \
> + rust.cmo \
> bindtests.cmo \
> errnostring.cmo \
> customize.cmo \
> diff --git a/generator/bindtests.ml b/generator/bindtests.ml
> index 58d7897b3..e88e71c8a 100644
> --- a/generator/bindtests.ml
> +++ b/generator/bindtests.ml
> @@ -983,6 +983,72 @@ and generate_php_bindtests () =
>
> dump "bindtests"
>
> +and generate_rust_bindtests () =
> + generate_header CStyle GPLv2plus;
> +
> + pr "extern crate guestfs;\n";
> + pr "use guestfs::*;\n";
> + pr "use std::default::Default;\n";
> + pr "\n";
> + pr "fn main() {\n";
> + pr " let g = match Handle::create() {\n";
> + pr " Ok(g) => g,\n";
> + pr " Err(e) => panic!(format!(\" could not create handle {}\",
> e)),\n";
> + pr " };\n";
> + generate_lang_bindtests (
> + fun f args optargs ->
> + pr " g.%s(" f;
> + let needs_comma = ref false in
> + List.iter (
> + fun arg ->
> + if !needs_comma then pr ", ";
> + needs_comma := true;
> + match arg with
> + | CallString s -> pr "\"%s\"" s
> + | CallOptString None -> pr "None"
> + | CallOptString (Some s) -> pr "Some(\"%s\")" s
> + | CallStringList xs ->
> + pr "&vec![%s]"
> + (String.concat ", " (List.map (sprintf "\"%s\"") xs))
> + | CallInt i -> pr "%d" i
> + | CallInt64 i -> pr "%Ldi64" i
> + | CallBool b -> pr "%b" b
> + | CallBuffer s ->
> + let f = fun x -> sprintf "%d" (Char.code x) in
> + pr "&[%s]"
> + (String.concat ", " (List.map f (String.explode s)))
> + ) args;
> + if !needs_comma then pr ", ";
> + (match optargs with
> + | None -> pr "Default::default()"
> + | Some optargs ->
> + pr "%sOptArgs{" (Rust.snake2caml f);
> + needs_comma := false;
> + List.iter (
> + fun optarg ->
> + if !needs_comma then pr ", ";
> + needs_comma := true;
> + match optarg with
> + | CallOBool (n, v) ->
> + pr "%s: Some(%b)" n v
> + | CallOInt (n, v) ->
> + pr "%s: Some(%d)" n v
> + | CallOInt64 (n, v) ->
> + pr "%s: Some(%Ldi64)" n v
> + | CallOString (n, v) ->
> + pr "%s: Some(\"%s\")" n v
> + | CallOStringList (n, xs) ->
> + pr "%s: Some(&[%s])"
> + n (String.concat ", " (List.map (sprintf "\"%s\"") xs))
> + ) optargs;
> + if !needs_comma then pr ", ";
> + pr ".. Default::default()}";
> + );
> + pr ").expect(\"failed to run\");\n";
> + );
> + pr " println!(\"EOF\");\n";
> + pr "}\n";
> +
> (* Language-independent bindings tests - we do it this way to
> * ensure there is parity in testing bindings across all languages.
> *)
> diff --git a/generator/bindtests.mli b/generator/bindtests.mli
> index 6f469b3a1..0e18a4c44 100644
> --- a/generator/bindtests.mli
> +++ b/generator/bindtests.mli
> @@ -28,3 +28,4 @@ val generate_perl_bindtests : unit -> unit
> val generate_php_bindtests : unit -> unit
> val generate_python_bindtests : unit -> unit
> val generate_ruby_bindtests : unit -> unit
> +val generate_rust_bindtests : unit -> unit
> diff --git a/generator/main.ml b/generator/main.ml
> index acacfb9e4..80000b1e3 100644
> --- a/generator/main.ml
> +++ b/generator/main.ml
> @@ -363,6 +363,11 @@ Run it from the top source directory using the command
> output_to "customize/customize-options.pod"
> Customize.generate_customize_options_pod;
>
> + output_to "rust/src/guestfs.rs"
> + Rust.generate_rust;
> + output_to "rust/src/bin/bindtests.rs"
> + Bindtests.generate_rust_bindtests;
> +
> (* Generate the list of files generated -- last. *)
> printf "generated %d lines of code\n" (get_lines_generated ());
> let files = List.sort compare (get_files_generated ()) in
> diff --git a/generator/rust.ml b/generator/rust.ml
> new file mode 100644
> index 000000000..09ca89982
> --- /dev/null
> +++ b/generator/rust.ml
> @@ -0,0 +1,564 @@
> +(* libguestfs
> + * Copyright (C) 2019 Red Hat Inc.
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, write to the Free Software
> + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
> 02110-1301 USA
> +*)
> +
> +(* Please read generator/README first. *)
> +
> +open Std_utils
> +open Types
> +open Utils
> +open Pr
> +open Docstrings
> +open Optgroups
> +open Actions
> +open Structs
> +open C
> +open Events
> +
> +(* Utilities for Rust *)
> +(* Are there corresponding functions to them? *)
> +(* Should they be placed in utils.ml? *)
> +let rec indent n = match n with
> + | x when x > 0 -> pr " "; indent (x - 1)
> + | _ -> ()
> +
> +(* split_on_char exists since OCaml 4.04 *)
> +(* but current requirements: >=4.01 *)
> +let split_on_char c = Str.split (Str.regexp (String.make 1 c))
> +
> +let snake2caml name =
> + let l = split_on_char '_' name in
> + let l = List.map (fun x -> String.capitalize_ascii x) l in
> + String.concat "" l
> +
> +(* because there is a function which contains 'unsafe' field *)
> +let black_list = ["unsafe"]
> +
> +let translate_bad_symbols s =
> + if List.exists (fun x -> s = x) black_list then
> + s ^ "_"
> + else
> + s
> +
> +let generate_rust () =
> + generate_header CStyle LGPLv2plus;
> +
> + pr "
> +use crate::base::*;
> +use crate::utils::*;
> +use crate::error::*;
> +use std::collections;
> +use std::convert;
> +use std::convert::TryFrom;
> +use std::ffi;
> +use std::os::raw::{c_char, c_int, c_void};
> +use std::ptr;
> +use std::slice;
> +
> +extern \"C\" {
> + fn free(buf: *const c_void);
> +}
> +";
> +
> + List.iter (
> + fun { s_camel_name = name; s_name = c_name; s_cols = cols } ->
> + pr "\n";
> + pr "pub struct %s {\n" name;
> + List.iter (
> + function
> + | n, FChar -> pr " pub %s: i8,\n" n
> + | n, FString -> pr " pub %s: String,\n" n
> + | n, FBuffer -> pr " pub %s: Vec<u8>,\n" n
> + | n, FUInt32 -> pr " pub %s: u32,\n" n
> + | n, FInt32 -> pr " pub %s: i32,\n" n
> + | n, (FUInt64 | FBytes) -> pr " pub %s: u64,\n" n
> + | n, FInt64 -> pr " pub %s: i64,\n" n
> + | n, FUUID -> pr " pub %s: UUID,\n" n
> + | n, FOptPercent -> pr " pub %s: Option<f32>,\n" n
> + ) cols;
> + pr "}\n";
> + pr "#[repr(C)]\n";
> + pr "struct Raw%s {\n" name;
> + List.iter (
> + function
> + | n, FChar -> pr " %s: c_char,\n" n
> + | n, FString -> pr " %s: *const c_char,\n" n
> + | n, FBuffer ->
> + pr " %s_len: usize,\n" n;
> + pr " %s: *const c_char,\n" n;
> + | n, FUUID -> pr " %s: [u8; 32],\n" n
> + | n, FUInt32 -> pr " %s: u32,\n" n
> + | n, FInt32 -> pr " %s: i32,\n" n
> + | n, (FUInt64 | FBytes) -> pr " %s: u64,\n" n
> + | n, FInt64 -> pr " %s: i64,\n" n
> + | n, FOptPercent -> pr " %s: f32,\n" n
> + ) cols;
> + pr "}\n";
> + pr "\n";
> + pr "impl TryFrom<*const Raw%s> for %s {\n" name name;
> + pr " type Error = Error;\n";
> + pr " fn try_from(raw: *const Raw%s) -> Result<Self, Self::Error>
> {\n" name;
> + pr " Ok(unsafe {\n";
> + pr " %s {\n" name;
> + List.iter (
> + fun x ->
> + indent 4;
> + match x with
> + | n, FChar ->
> + pr "%s: (*raw).%s as i8,\n" n n;
> + | n, FString ->
> + pr "%s: {\n" n;
> + indent 5;
> + pr "let s = ffi::CStr::from_ptr((*raw).%s);\n" n;
> + indent 5;
> + pr "s.to_str()?.to_string()\n";
> + indent 4;
> + pr "},\n"
> + | n, FBuffer ->
> + pr "%s: slice::from_raw_parts((*raw).%s as *const u8,
> (*raw).%s_len).to_vec(),\n" n n n
> + | n, FUUID ->
> + pr "%s: UUID::new((*raw).%s),\n" n n
> + | n, (FUInt32 | FInt32 | FUInt64 | FInt64 | FBytes) ->
> + pr "%s: (*raw).%s,\n" n n
> + | n, FOptPercent ->
> + pr "%s: if (*raw).%s < 0.0 {\n" n n;
> + indent 4; pr " None\n";
> + indent 4; pr "} else {\n";
> + indent 4; pr " Some((*raw).%s)\n" n;
> + indent 4; pr"},\n"
> + ) cols;
> + pr " }\n";
> + pr " })\n";
> + pr " }\n";
> + pr "}\n"
> + ) external_structs;
> +
> + (* generate free functionf of structs *)
> + pr "\n";
> + pr "extern \"C\" {\n";
> + List.iter (
> + fun { s_camel_name = name; s_name = c_name; } ->
> + pr " #[allow(dead_code)]\n";
> + pr " fn guestfs_free_%s(v: *const Raw%s);\n" c_name name;
> + pr " #[allow(dead_code)]\n";
> + pr " fn guestfs_free_%s_list(l: *const RawList<Raw%s>);\n"
> c_name name;
> + ) external_structs;
> + pr "}\n";
> +
> + (* [Outline] There are three types for each optional structs: SOptArgs,
> + * CExprSOptArgs, RawSOptArgs.
> + * SOptArgs: for Rust bindings' API. This can be seen by bindings'
> users.
> + * CExprSOptArgs: Each field has C expression(e.g. CString, *const
> c_char)
> + * RawSOptArgs: Each field has raw pointers or integer values
> + *
> + * SOptArgs ---try_into()---> CExprSOptArgs ---to_raw()---> RawSOptArgs
> + *
> + * Note: direct translation from SOptArgs to RawSOptArgs will cause a
> memory
> + * management problem. Using into_raw/from_raw, this problem can be
> avoided,
> + * but it is complex to handle freeing memories manually in Rust
> because of
> + * panic/?/etc.
> + *)
> + (* generate structs for optional arguments *)
> + List.iter (
> + fun ({ name = name; shortdesc = shortdesc;
> + style = (ret, args, optargs) }) ->
> + let cname = snake2caml name in
> + let rec contains_ptr args = match args with
> + | [] -> false
> + | OString _ ::_
> + | OStringList _::_ -> true
> + | _::xs -> contains_ptr xs
> + in
> + let opt_life_parameter = if contains_ptr optargs then "<'a>" else
> "" in
> + if optargs <> [] then (
> + pr "\n";
> + pr "/* Optional Structs */\n";
> + pr "#[derive(Default)]\n";
> + pr "pub struct %sOptArgs%s {\n" cname opt_life_parameter;
> + List.iter (
> + fun optarg ->
> + let n = translate_bad_symbols (name_of_optargt optarg) in
> + match optarg with
> + | OBool _ ->
> + pr " pub %s: Option<bool>,\n" n
> + | OInt _ ->
> + pr " pub %s: Option<i32>,\n" n
> + | OInt64 _ ->
> + pr " pub %s: Option<i64>,\n" n
> + | OString _ ->
> + pr " pub %s: Option<&'a str>,\n" n
> + | OStringList _ ->
> + pr " pub %s: Option<&'a [&'a str]>,\n" n
> + ) optargs;
> + pr "}\n\n";
> +
> + pr "struct CExpr%sOptArgs {\n" cname;
> + List.iter (
> + fun optarg ->
> + let n = translate_bad_symbols (name_of_optargt optarg) in
> + match optarg with
> + | OBool _ | OInt _ ->
> + pr " %s: Option<c_int>,\n" n
> + | OInt64 _ ->
> + pr " %s: Option<i64>,\n" n
> + | OString _ ->
> + pr " %s: Option<ffi::CString>,\n" n
> + | OStringList _ ->
> + (* buffers and their pointer vector *)
> + pr " %s: Option<(Vec<ffi::CString>, Vec<*const
> c_char>)>,\n" n
> + ) optargs;
> + pr "}\n\n";
> +
> + pr "impl%s TryFrom<%sOptArgs%s> for CExpr%sOptArgs {\n"
> + opt_life_parameter cname opt_life_parameter cname;
> + pr " type Error = Error;\n";
> + pr " fn try_from(optargs: %sOptArgs%s) -> Result<Self,
> Self::Error> {\n" cname opt_life_parameter;
> + pr " Ok(CExpr%sOptArgs {\n" cname;
> + List.iteri (
> + fun index optarg ->
> + let n = translate_bad_symbols (name_of_optargt optarg) in
> + match optarg with
> + | OBool _ ->
> + pr " %s: optargs.%s.map(|b| if b { 1 } else { 0
> }),\n" n n;
> + | OInt _ | OInt64 _ ->
> + pr " %s: optargs.%s, \n" n n;
> + | OString _ ->
> + pr " %s: optargs.%s.map(|v|
> ffi::CString::new(v)).transpose()?,\n" n n;
> + | OStringList _ ->
> + pr " %s: optargs.%s.map(\n" n n;
> + pr " |v| Ok::<_, Error>({\n";
> + pr " let v =
> arg_string_list(v)?;\n";
> + pr " let mut w =
> (&v).into_iter()\n";
> + pr " .map(|v|
> v.as_ptr())\n";
> + pr "
> .collect::<Vec<_>>();\n";
> + pr " w.push(ptr::null());\n";
> + pr " (v, w)\n";
> + pr " })\n";
> + pr " ).transpose()?,\n";
> + ) optargs;
> + pr " })\n";
> + pr " }\n";
> + pr "}\n";
> +
> + (* raw struct for C bindings *)
> + pr "#[repr(C)]\n";
> + pr "struct Raw%sOptArgs {\n" cname;
> + pr " bitmask: u64,\n";
> + List.iter (
> + fun optarg ->
> + let n = translate_bad_symbols (name_of_optargt optarg) in
> + match optarg with
> + | OBool _ ->
> + pr " %s: c_int,\n" n
> + | OInt _ ->
> + pr " %s: c_int,\n" n
> + | OInt64 _ ->
> + pr " %s: i64,\n" n
> + | OString _ ->
> + pr " %s: *const c_char,\n" n
> + | OStringList _ ->
> + pr " %s: *const *const c_char,\n" n
> + ) optargs;
> + pr "}\n\n";
> +
> + pr "impl convert::From<&CExpr%sOptArgs> for Raw%sOptArgs {\n"
> + cname cname;
> + pr " fn from(optargs: &CExpr%sOptArgs) -> Self {\n" cname;
> + pr " let mut bitmask = 0;\n";
> + pr " Raw%sOptArgs {\n" cname;
> + List.iteri (
> + fun index optarg ->
> + let n = translate_bad_symbols (name_of_optargt optarg) in
> + match optarg with
> + | OBool _ | OInt _ | OInt64 _ ->
> + pr " %s: if let Some(v) = optargs.%s {\n" n n;
> + pr " bitmask |= 1 << %d;\n" index;
> + pr " v\n";
> + pr " } else {\n";
> + pr " 0\n";
> + pr " },\n";
> + | OString _ ->
> + pr " %s: if let Some(ref v) = optargs.%s {\n" n n;
> + pr " bitmask |= 1 << %d;\n" index;
> + pr " v.as_ptr()\n";
> + pr " } else {\n";
> + pr " ptr::null()\n";
> + pr " },\n";
> + | OStringList _ ->
> + pr " %s: if let Some((_, ref v)) = optargs.%s {\n" n
> n;
> + pr " bitmask |= 1 << %d;\n" index;
> + pr " v.as_ptr()\n";
> + pr " } else {\n";
> + pr " ptr::null()\n";
> + pr " },\n";
> + ) optargs;
> + pr " bitmask,\n";
> + pr " }\n";
> + pr " }\n";
> + pr "}\n";
> + );
> + ) (actions |> external_functions |> sort);
> +
> + (* extern C APIs *)
> + pr "extern \"C\" {\n";
> + List.iter (
> + fun ({ name = name; shortdesc = shortdesc;
> + style = (ret, args, optargs) } as f) ->
> + let cname = snake2caml name in
> + pr " #[allow(non_snake_case)]\n";
> + pr " fn %s(g: *const guestfs_h" f.c_function;
> + List.iter (
> + fun arg ->
> + pr ", ";
> + match arg with
> + | Bool n -> pr "%s: c_int" n
> + | String (_, n) -> pr "%s: *const c_char" n
> + | OptString n -> pr "%s: *const c_char" n
> + | Int n -> pr "%s: c_int" n
> + | Int64 n -> pr "%s: i64" n
> + | Pointer (_, n) -> pr "%s: *const ffi::c_void" n
> + | StringList (_, n) -> pr "%s: *const *const c_char" n
> + | BufferIn n -> pr "%s: *const c_char, %s_len: usize" n n
> + ) args;
> + (match ret with
> + | RBufferOut _ -> pr ", size: *const usize"
> + | _ -> ()
> + );
> + if optargs <> [] then
> + pr ", optarg: *const Raw%sOptArgs" cname;
> +
> + pr ") -> ";
> +
> + (match ret with
> + | RErr | RInt _ | RBool _ -> pr "c_int"
> + | RInt64 _ -> pr "i64"
> + | RConstString _ | RString _ | RConstOptString _ -> pr "*const
> c_char"
> + | RBufferOut _ -> pr "*const u8"
> + | RStringList _ | RHashtable _-> pr "*const *const c_char"
> + | RStruct (_, n) ->
> + let n = camel_name_of_struct n in
> + pr "*const Raw%s" n
> + | RStructList (_, n) ->
> + let n = camel_name_of_struct n in
> + pr "*const RawList<Raw%s>" n
> + );
> + pr ";\n";
> +
> + ) (actions |> external_functions |> sort);
> + pr "}\n";
> +
> +
> + pr "impl Handle {\n";
> + List.iter (
> + fun ({ name = name; shortdesc = shortdesc; longdesc = longdesc;
> + style = (ret, args, optargs) } as f) ->
> + let cname = snake2caml name in
> + pr " /// %s\n" shortdesc;
> + pr " #[allow(non_snake_case)]\n";
> + pr " pub fn %s" name;
> +
> + (* generate arguments *)
> + pr "(&self, ";
> +
> + let comma = ref false in
> + List.iter (
> + fun arg ->
> + if !comma then pr ", ";
> + comma := true;
> + match arg with
> + | Bool n -> pr "%s: bool" n
> + | Int n -> pr "%s: i32" n
> + | Int64 n -> pr "%s: i64" n
> + | String (_, n) -> pr "%s: &str" n
> + | OptString n -> pr "%s: Option<&str>" n
> + | StringList (_, n) -> pr "%s: &[&str]" n
> + | BufferIn n -> pr "%s: &[u8]" n
> + | Pointer (_, n) -> pr "%s: *mut c_void" n
> + ) args;
> + if optargs <> [] then (
> + if !comma then pr ", ";
> + comma := true;
> + pr "optargs: %sOptArgs" cname
> + );
> + pr ")";
> +
> + (* generate return type *)
> + pr " -> Result<";
> + (match ret with
> + | RErr -> pr "()"
> + | RInt _ -> pr "i32"
> + | RInt64 _ -> pr "i64"
> + | RBool _ -> pr "bool"
> + | RConstString _ -> pr "&'static str"
> + | RString _ -> pr "String"
> + | RConstOptString _ -> pr "Option<&'static str>"
> + | RStringList _ -> pr "Vec<String>"
> + | RStruct (_, sn) ->
> + let sn = camel_name_of_struct sn in
> + pr "%s" sn
> + | RStructList (_, sn) ->
> + let sn = camel_name_of_struct sn in
> + pr "Vec<%s>" sn
> + | RHashtable _ -> pr "collections::HashMap<String, String>"
> + | RBufferOut _ -> pr "Vec<u8>");
> + pr ", Error> {\n";
> +
> +
> + let _pr = pr in
> + let pr fs = indent 2; pr fs in
> + List.iter (
> + function
> + | Bool n ->
> + pr "let %s = if %s { 1 } else { 0 };\n" n n
> + | String (_, n) ->
> + pr "let c_%s = ffi::CString::new(%s)?;\n" n n;
> + | OptString n ->
> + pr "let c_%s = %s.map(|s|
> ffi::CString::new(s)).transpose()?;\n" n n;
> + | StringList (_, n) ->
> + pr "let c_%s_v = arg_string_list(%s)?;\n" n n;
> + pr "let mut c_%s = (&c_%s_v).into_iter().map(|v|
> v.as_ptr()).collect::<Vec<_>>();\n" n n;
> + pr "c_%s.push(ptr::null());\n" n;
> + | BufferIn n ->
> + pr "let c_%s_len = %s.len();\n" n n;
> + pr "let c_%s = unsafe {
> ffi::CString::from_vec_unchecked(%s.to_vec())};\n" n n;
> + | Int _ | Int64 _ | Pointer _ -> ()
> + ) args;
> +
> + (match ret with
> + | RBufferOut _ ->
> + pr "let mut size = 0usize;\n"
> + | _ -> ()
> + );
> +
> + if optargs <> [] then (
> + pr "let optargs_cexpr = CExpr%sOptArgs::try_from(optargs)?;\n"
> cname;
> + );
> +
> + pr "\n";
> +
> + pr "let r = unsafe { %s(self.g" f.c_function;
> + let pr = _pr in
> + List.iter (
> + fun arg ->
> + pr ", ";
> + match arg with
> + | String (_, n) -> pr "(&c_%s).as_ptr()" n
> + | OptString n -> pr "match &c_%s { Some(ref s) => s.as_ptr(),
> None => ptr::null() }\n" n
> + | StringList (_, n) -> pr "(&c_%s).as_ptr() as *const *const
> c_char" n
> + | Bool n | Int n | Int64 n | Pointer (_, n) -> pr "%s" n
> + | BufferIn n -> pr "(&c_%s).as_ptr(), c_%s_len" n n
> + ) args;
> + (match ret with
> + | RBufferOut _ -> pr ", &mut size as *mut usize"
> + | _ -> ()
> + );
> + if optargs <> [] then (
> + pr ", &(Raw%sOptArgs::from(&optargs_cexpr)) as *const
> Raw%sOptArgs"
> + cname cname;
> + );
> + pr ") };\n";
> +
> + let _pr = pr in
> + let pr fs = indent 2; pr fs in
> + (match errcode_of_ret ret with
> + | `CannotReturnError -> ()
> + | `ErrorIsMinusOne ->
> + pr "if r == -1 {\n";
> + pr " return Err(self.get_error_from_handle(\"%s\"));\n" name;
> + pr "}\n"
> + | `ErrorIsNULL ->
> + pr "if r.is_null() {\n";
> + pr " return Err(self.get_error_from_handle(\"%s\"));\n" name;
> + pr "}\n"
> + );
> +
> + (* This part is not required, but type system will guarantee that
> + * the buffers are still alive. This is useful because Rust cannot
> + * know whether raw pointers used above are alive or not.
> + *)
> + List.iter (
> + function
> + | Bool _ | Int _ | Int64 _ | Pointer _ -> ()
> + | String (_, n)
> + | OptString n
> + | BufferIn n -> pr "drop(c_%s);\n" n;
> + | StringList (_, n) ->
> + pr "drop(c_%s);\n" n;
> + pr "drop(c_%s_v);\n" n;
> + ) args;
> + if optargs <> [] then (
> + pr "drop(optargs_cexpr);\n";
> + );
> +
> + pr "Ok(";
> + let pr = _pr in
> + let pr3 fs = indent 3; pr fs in
> + (match ret with
> + | RErr -> pr "()"
> + | RInt _ | RInt64 _ -> pr "r"
> + | RBool _ -> pr "r != 0"
> + | RConstString _ ->
> + pr "unsafe{ ffi::CStr::from_ptr(r) }.to_str()?"
> + | RString _ ->
> + pr "{\n";
> + pr3 "let s = unsafe { ffi::CStr::from_ptr(r) };\n";
> + pr3 "unsafe { free(r as *const c_void) };\n";
> + pr3 "s.to_str()?.to_string()\n";
> + indent 2; pr "}";
> + | RConstOptString _ ->
> + pr "if r.is_null() {\n";
> + pr3 "None\n";
> + indent 2; pr "} else {\n";
> + pr3 "Some(unsafe { ffi::CStr::from_ptr(r) }.to_str()?)\n";
> + indent 2; pr "}";
> + | RStringList _ ->
> + pr "{\n";
> + pr3 "let s = string_list(r);\n";
> + pr3 "free_string_list(r);\n";
> + pr3 "s?\n";
> + indent 2; pr "}";
> + | RStruct (_, n) ->
> + let sn = camel_name_of_struct n in
> + pr "{\n";
> + pr3 "let s = %s::try_from(r);\n" sn;
> + pr3 "unsafe { guestfs_free_%s(r) };\n" n;
> + pr3 "s?\n";
> + indent 2; pr "}";
> + | RStructList (_, n) ->
> + let sn = camel_name_of_struct n in
> + pr "{\n";
> + pr3 "let l = struct_list::<Raw%s, %s>(r);\n" sn sn;
> + pr3 "unsafe { guestfs_free_%s_list(r) };\n" n;
> + pr3 "l?\n";
> + indent 2; pr "}";
> + | RHashtable _ ->
> + pr "{\n";
> + pr3 "let h = hashmap(r);\n";
> + pr3 "free_string_list(r);\n";
> + pr3 "h?\n";
> + indent 2; pr "}";
> + | RBufferOut _ ->
> + pr "{\n";
> + pr3 "let s = unsafe { slice::from_raw_parts(r, size)
> }.to_vec();\n";
> + pr3 "unsafe { free(r as *const c_void) } ;\n";
> + pr3 "s\n";
> + indent 2; pr "}";
> + );
> + pr ")\n";
> + pr " }\n\n"
> + ) (actions |> external_functions |> sort);
> + pr "}\n"
> diff --git a/generator/rust.mli b/generator/rust.mli
> new file mode 100644
> index 000000000..5410286c8
> --- /dev/null
> +++ b/generator/rust.mli
> @@ -0,0 +1,22 @@
> +(* libguestfs
> + * Copyright (C) 2009-2019 Red Hat Inc.
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, write to the Free Software
> + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
> 02110-1301 USA
> +*)
> +
> +val generate_rust: unit -> unit
> +
> +(* for bindtests.ml *)
> +val snake2caml: string -> string
> diff --git a/m4/guestfs-rust.m4 b/m4/guestfs-rust.m4
> new file mode 100644
> index 000000000..48eee433e
> --- /dev/null
> +++ b/m4/guestfs-rust.m4
> @@ -0,0 +1,33 @@
> +# libguestfs
> +# Copyright (C) 2009-2019 Red Hat Inc.
> +#
> +# This program is free software; you can redistribute it and/or modify
> +# it under the terms of the GNU General Public License as published by
> +# the Free Software Foundation; either version 2 of the License, or
> +# (at your option) any later version.
> +#
> +# This program is distributed in the hope that it will be useful,
> +# but WITHOUT ANY WARRANTY; without even the implied warranty of
> +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> +# GNU General Public License for more details.
> +#
> +# You should have received a copy of the GNU General Public License
> +# along with this program; if not, write to the Free Software
> +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
> 02110-1301 USA.
> +
> +dnl Rust
> +AC_ARG_ENABLE([rust],
> + AS_HELP_STRING([--disable-rust], [disable Rust language bindings]),
> + [],
> + [enable_rust=yes])
> +AS_IF([test "x$enable_rust" != "xno"],[
> + AC_CHECK_PROG([RUSTC],[rustc],[rustc],[no])
> + AC_CHECK_PROG([CARGO],[cargo],[cargo],[no])
> +
> + AS_IF([test "x$RUSTC" == "xno"], [AC_MSG_WARN([rustc not found])])
> + AS_IF([test "x$CARGO" == "xno"], [AC_MSG_WARN([cargo not found])])
> +],[
> + RUSTC=no
> + CARGO=no
> + ])
> +AM_CONDITIONAL([HAVE_RUST],[test "x$RUSTC" != "xno" && test "x$CARGO" !=
> "xno"])
> diff --git a/run.in b/run.in
> index 488e1b937..301b02664 100755
> --- a/run.in
> +++ b/run.in
> @@ -201,6 +201,15 @@ else
> fi
> export CGO_LDFLAGS
>
> +# For rust
> +export RUST="@RUST@"
> +if [ -z "$RUSTFLAGS" ]; then
> + RUSTFLAGS="-C link-args=-L$b/lib/.libs"
> +else
> + RUSTFLAGS="$RUSTFLAGS -C link-args=-L$b/lib/.libs"
> +fi
> +export RUSTFLAGS
> +
> # For GObject, Javascript and friends.
> export GJS="@GJS@"
> prepend GI_TYPELIB_PATH "$b/gobject"
> diff --git a/rust/.gitignore b/rust/.gitignore
> new file mode 100644
> index 000000000..693699042
> --- /dev/null
> +++ b/rust/.gitignore
> @@ -0,0 +1,3 @@
> +/target
> +**/*.rs.bk
> +Cargo.lock
> diff --git a/rust/Cargo.toml.in b/rust/Cargo.toml.in
> new file mode 100644
> index 000000000..e25dfe768
> --- /dev/null
> +++ b/rust/Cargo.toml.in
> @@ -0,0 +1,6 @@
> +[package]
> +name = "guestfs"
> +version = "@VERSION@"
> +edition = "2018"
> +
> +[dependencies]
> diff --git a/rust/Makefile.am b/rust/Makefile.am
> new file mode 100644
> index 000000000..2ec4f7d08
> --- /dev/null
> +++ b/rust/Makefile.am
> @@ -0,0 +1,42 @@
> +# libguestfs rust bindings
> +# Copyright (C) 2019 Red Hat Inc.
> +#
> +# This program is free software; you can redistribute it and/or modify
> +# it under the terms of the GNU General Public License as published by
> +# the Free Software Foundation; either version 2 of the License, or
> +# (at your option) any later version.
> +#
> +# This program is distributed in the hope that it will be useful,
> +# but WITHOUT ANY WARRANTY; without even the implied warranty of
> +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> +# GNU General Public License for more details.
> +#
> +# You should have received a copy of the GNU General Public License
> +# along with this program; if not, write to the Free Software
> +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
> 02110-1301 USA.
> +
> +include $(top_srcdir)/subdir-rules.mk
> +
> +generator_built = \
> + src/bin/bindtests.rs \
> + src/lib.rs
> +
> +EXTRA_DIST = \
> + .gitignore \
> + $(generator_built) \
> + tests/*.rs \
> + Cargo.toml \
> + Cargo.lock \
> + run-bindtests \
> + run-tests
> +
> +if HAVE_RUST
> +
> +all: src/lib.rs
> + $(top_builddir)/run $(CARGO) build --release
> +
> +TESTS = run-bindtests run-tests
> +
> +CLEANFILES += target/*~
> +
> +endif
> diff --git a/rust/run-bindtests b/rust/run-bindtests
> new file mode 100755
> index 000000000..55484a2c7
> --- /dev/null
> +++ b/rust/run-bindtests
> @@ -0,0 +1,23 @@
> +#!/bin/sh -
> +# libguestfs Rust 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..9a5e7a1e4
> --- /dev/null
> +++ b/rust/run-tests
> @@ -0,0 +1,21 @@
> +#!/bin/sh -
> +# libguestfs Rust tests
> +# Copyright (C) 2013 Red Hat Inc.
> +#
> +# This program is free software; you can redistribute it and/or modify
> +# it under the terms of the GNU General Public License as published by
> +# the Free Software Foundation; either version 2 of the License, or
> +# (at your option) any later version.
> +#
> +# This program is distributed in the hope that it will be useful,
> +# but WITHOUT ANY WARRANTY; without even the implied warranty of
> +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> +# GNU General Public License for more details.
> +#
> +# You should have received a copy of the GNU General Public License
> +# along with this program; if not, write to the Free Software
> +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
> 02110-1301 USA.
> +
> +set -e
> +
> +$CARGO test
> diff --git a/rust/src/.gitkeep b/rust/src/.gitkeep
> new file mode 100644
> index 000000000..e69de29bb
> diff --git a/rust/src/base.rs b/rust/src/base.rs
> new file mode 100644
> index 000000000..db64284e7
> --- /dev/null
> +++ b/rust/src/base.rs
> @@ -0,0 +1,125 @@
> +/* libguestfs generated file
> + * WARNING: THIS FILE IS GENERATED
> + * from the code in the generator/ subdirectory.
> + * ANY CHANGES YOU MAKE TO THIS FILE WILL BE LOST.
> + *
> + * Copyright (C) 2009-2019 Red Hat Inc.
> + *
> + * This library is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU Lesser General Public
> + * License as published by the Free Software Foundation; either
> + * version 2 of the License, or (at your option) any later version.
> + *
> + * This library 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
> + * Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public
> + * License along with this library; if not, write to the Free Software
> + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
> 02110-1301 USA
> + */
> +
> +use std::str;
> +
> +#[allow(non_camel_case_types)]
> +#[repr(C)]
> +pub(crate) struct guestfs_h {
> + _unused: [u32; 0],
> +}
> +
> +#[link(name = "guestfs")]
> +extern "C" {
> + fn guestfs_create() -> *mut guestfs_h;
> + fn guestfs_create_flags(flags: i64) -> *mut guestfs_h;
> + fn guestfs_close(g: *mut guestfs_h);
> +}
> +
> +const GUESTFS_CREATE_NO_ENVIRONMENT: i64 = 1;
> +const GUESTFS_CREATE_NO_CLOSE_ON_EXIT: i64 = 2;
> +
> +pub struct Handle {
> + pub(crate) g: *mut guestfs_h,
> +}
> +
> +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 })
> + }
> + }
> +}
> +
> +impl Drop for Handle {
> + fn drop(&mut self) {
> + unsafe { guestfs_close(self.g) }
> + }
> +}
> +
> +pub struct CreateFlags {
> + create_no_environment_flag: bool,
> + create_no_close_on_exit_flag: bool,
> +}
> +
> +impl CreateFlags {
> + pub fn none() -> CreateFlags {
> + CreateFlags {
> + create_no_environment_flag: false,
> + create_no_close_on_exit_flag: false,
> + }
> + }
> +
> + pub fn new() -> CreateFlags {
> + CreateFlags::none()
> + }
> +
> + pub fn create_no_environment(mut self, flag: bool) -> CreateFlags {
> + self.create_no_environment_flag = flag;
> + self
> + }
> +
> + pub fn create_no_close_on_exit_flag(mut self, flag: bool) ->
> CreateFlags {
> + self.create_no_close_on_exit_flag = flag;
> + self
> + }
> +
> + unsafe fn to_libc_int(self) -> i64 {
> + let mut flag = 0;
> + flag |= if self.create_no_environment_flag {
> + GUESTFS_CREATE_NO_ENVIRONMENT
> + } else {
> + 0
> + };
> + flag |= if self.create_no_close_on_exit_flag {
> + GUESTFS_CREATE_NO_CLOSE_ON_EXIT
> + } else {
> + 0
> + };
> + flag
> + }
> +}
> +
> +pub struct UUID {
> + uuid: [u8; 32],
> +}
> +
> +impl UUID {
> + pub(crate) fn new(uuid: [u8; 32]) -> UUID {
> + UUID { uuid }
> + }
> + pub fn to_bytes(self) -> [u8; 32] {
> + self.uuid
> + }
> +}
> diff --git a/rust/src/bin/.gitkeep b/rust/src/bin/.gitkeep
> new file mode 100644
> index 000000000..e69de29bb
> diff --git a/rust/src/error.rs b/rust/src/error.rs
> new file mode 100644
> index 000000000..047fae7a1
> --- /dev/null
> +++ b/rust/src/error.rs
> @@ -0,0 +1,68 @@
> +/* libguestfs Rust bindings
> + * Copyright (C) 2009-2019 Red Hat Inc.
> + *
> + * This library is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU Lesser General Public
> + * License as published by the Free Software Foundation; either
> + * version 2 of the License, or (at your option) any later version.
> + *
> + * This library 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
> + * Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public
> + * License along with this library; if not, write to the Free Software
> + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
> 02110-1301 USA
> + */
> +
> +use crate::base::*;
> +use std::convert;
> +use std::ffi;
> +use std::os::raw::{c_char, c_int};
> +use std::str;
> +
> +#[link(name = "guestfs")]
> +extern "C" {
> + fn guestfs_last_error(g: *mut guestfs_h) -> *const c_char;
> + fn guestfs_last_errno(g: *mut guestfs_h) -> c_int;
> +}
> +
> +#[derive(Debug)]
> +pub struct APIError {
> + operation: &'static str,
> + message: String,
> + errno: i32,
> +}
> +
> +#[derive(Debug)]
> +pub enum Error {
> + API(APIError),
> + IllegalString(ffi::NulError),
> + Utf8Error(str::Utf8Error),
> +}
> +
> +impl convert::From<ffi::NulError> for Error {
> + fn from(error: ffi::NulError) -> Self {
> + Error::IllegalString(error)
> + }
> +}
> +
> +impl convert::From<str::Utf8Error> for Error {
> + fn from(error: str::Utf8Error) -> Self {
> + Error::Utf8Error(error)
> + }
> +}
> +
> +impl Handle {
> + pub(crate) fn get_error_from_handle(&self, operation: &'static str)
> -> Error {
> + let c_msg = unsafe { guestfs_last_error(self.g) };
> + let message = unsafe {
> ffi::CStr::from_ptr(c_msg).to_str().unwrap().to_string() };
> + let errno = unsafe { guestfs_last_errno(self.g) };
> + Error::API(APIError {
> + operation,
> + message,
> + errno,
> + })
> + }
> +}
> diff --git a/rust/src/lib.rs b/rust/src/lib.rs
> new file mode 100644
> index 000000000..5111e2546
> --- /dev/null
> +++ b/rust/src/lib.rs
> @@ -0,0 +1,7 @@
> +mod base;
> +mod error;
> +mod guestfs;
> +mod utils;
> +
> +pub use crate::base::*;
> +pub use crate::guestfs::*;
> diff --git a/rust/src/utils.rs b/rust/src/utils.rs
> new file mode 100644
> index 000000000..ac1996e91
> --- /dev/null
> +++ b/rust/src/utils.rs
> @@ -0,0 +1,136 @@
> +/* libguestfs Rust bindings
> + * Copyright (C) 2009-2019 Red Hat Inc.
> + *
> + * This library is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU Lesser General Public
> + * License as published by the Free Software Foundation; either
> + * version 2 of the License, or (at your option) any later version.
> + *
> + * This library 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
> + * Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public
> + * License along with this library; if not, write to the Free Software
> + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
> 02110-1301 USA
> + */
> +
> +use crate::error::*;
> +use std::collections;
> +use std::convert::TryFrom;
> +use std::ffi;
> +use std::os::raw::{c_char, c_void};
> +
> +extern "C" {
> + fn free(buf: *const c_void);
> +}
> +
> +pub(crate) struct NullTerminatedIter<T: Copy + Clone> {
> + p: *const *const T,
> +}
> +
> +impl<T: Copy + Clone> NullTerminatedIter<T> {
> + pub(crate) fn new(p: *const *const T) -> NullTerminatedIter<T> {
> + NullTerminatedIter { p }
> + }
> +}
> +
> +impl<T: Copy + Clone> Iterator for NullTerminatedIter<T> {
> + type Item = *const T;
> + fn next(&mut self) -> Option<*const T> {
> + let r = unsafe { *(self.p) };
> + if r.is_null() {
> + None
> + } else {
> + self.p = unsafe { self.p.offset(1) };
> + Some(r)
> + }
> + }
> +}
> +
> +#[repr(C)]
> +pub(crate) struct RawList<T> {
> + size: u32,
> + ptr: *const T,
> +}
> +
> +pub(crate) 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)
> + }
> + }
> +}
> +
> +pub(crate) fn arg_string_list(v: &[&str]) -> Result<Vec<ffi::CString>,
> Error> {
> + let mut w = Vec::new();
> + for x in v.iter() {
> + let y: &str = x;
> + w.push(ffi::CString::new(y)?);
> + }
> + Ok(w)
> +}
> +
> +pub(crate) fn free_string_list(l: *const *const c_char) {
> + for buf in NullTerminatedIter::new(l) {
> + unsafe { free(buf as *const c_void) };
> + }
> + unsafe { free(l as *const c_void) };
> +}
> +
> +pub(crate) fn hashmap(
> + l: *const *const c_char,
> +) -> Result<collections::HashMap<String, String>, Error> {
> + let mut map = collections::HashMap::new();
> + let mut iter = NullTerminatedIter::new(l);
> + while let Some(key) = iter.next() {
> + if let Some(val) = iter.next() {
> + let key = unsafe { ffi::CStr::from_ptr(key) }.to_str()?;
> + let val = unsafe { ffi::CStr::from_ptr(val) }.to_str()?;
> + map.insert(key.to_string(), val.to_string());
> + } else {
> + // Internal Error -> panic
> + panic!("odd number of items in hash table");
> + }
> + }
> + Ok(map)
> +}
> +
> +pub(crate) fn struct_list<T, S: TryFrom<*const T, Error = Error>>(
> + l: *const RawList<T>,
> +) -> Result<Vec<S>, Error> {
> + let mut v = Vec::new();
> + for x in unsafe { &*l }.iter() {
> + v.push(S::try_from(x)?);
> + }
> + Ok(v)
> +}
> +
> +pub(crate) fn string_list(l: *const *const c_char) -> Result<Vec<String>,
> Error> {
> + let mut v = Vec::new();
> + for x in NullTerminatedIter::new(l) {
> + let s = unsafe { ffi::CStr::from_ptr(x) }.to_str()?;
> + v.push(s.to_string());
> + }
> + Ok(v)
> +}
> diff --git a/rust/tests/.gitkeep b/rust/tests/.gitkeep
> new file mode 100644
> index 000000000..e69de29bb
> diff --git a/rust/tests/010_load.rs b/rust/tests/010_load.rs
> new file mode 100644
> index 000000000..4cb43f2c1
> --- /dev/null
> +++ b/rust/tests/010_load.rs
> @@ -0,0 +1,24 @@
> +/* libguestfs Rust bindings
> +Copyright (C) 2009-2019 Red Hat Inc.
> +
> +This program is free software; you can redistribute it and/or modify
> +it under the terms of the GNU General Public License as published by
> +the Free Software Foundation; either version 2 of the License, or
> +(at your option) any later version.
> +
> +This program is distributed in the hope that it will be useful,
> +but WITHOUT ANY WARRANTY; without even the implied warranty of
> +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> +GNU General Public License for more details.
> +
> +You should have received a copy of the GNU General Public License
> +along with this program; if not, write to the Free Software
> +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
> USA.
> +*/
> +
> +extern crate guestfs;
> +
> +#[test]
> +fn load() {
> + // nop
> +}
> diff --git a/rust/tests/020_create.rs b/rust/tests/020_create.rs
> new file mode 100644
> index 000000000..13acbc7d7
> --- /dev/null
> +++ b/rust/tests/020_create.rs
> @@ -0,0 +1,24 @@
> +/* libguestfs Rust bindings
> +Copyright (C) 2009-2019 Red Hat Inc.
> +
> +This program is free software; you can redistribute it and/or modify
> +it under the terms of the GNU General Public License as published by
> +the Free Software Foundation; either version 2 of the License, or
> +(at your option) any later version.
> +
> +This program is distributed in the hope that it will be useful,
> +but WITHOUT ANY WARRANTY; without even the implied warranty of
> +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> +GNU General Public License for more details.
> +
> +You should have received a copy of the GNU General Public License
> +along with this program; if not, write to the Free Software
> +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
> USA.
> +*/
> +
> +extern crate guestfs;
> +
> +#[test]
> +fn create() {
> + guestfs::Handle::create().unwrap();
> +}
> diff --git a/rust/tests/030_create_flags.rs b/rust/tests/
> 030_create_flags.rs
> new file mode 100644
> index 000000000..df3190d4c
> --- /dev/null
> +++ b/rust/tests/030_create_flags.rs
> @@ -0,0 +1,29 @@
> +/* libguestfs Rust bindings
> +Copyright (C) 2009-2019 Red Hat Inc.
> +
> +This program is free software; you can redistribute it and/or modify
> +it under the terms of the GNU General Public License as published by the
> Free Software Foundation; either version 2 of the License, or
> +(at your option) any later version.
> +
> +This program is distributed in the hope that it will be useful,
> +but WITHOUT ANY WARRANTY; without even the implied warranty of
> +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> +GNU General Public License for more details.
> +
> +You should have received a copy of the GNU General Public License
> +along with this program; if not, write to the Free Software
> +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
> USA.
> +*/
> +
> +extern crate guestfs;
> +
> +use guestfs::*;
> +
> +#[test]
> +fn create_flags() {
> + let _h =
> Handle::create_flags(CreateFlags::none()).expect("create_flags fail");
> + // TODO: Add parse_environment to check the flag is created correctly
> + let flags = CreateFlags::new().create_no_environment(true);
> + let _h = Handle::create_flags(flags).expect("create_flags fail");
> + // TODO: Add parse_environment to check the flag is created correctly
> +}
> diff --git a/rust/tests/040_create_multiple.rs b/rust/tests/
> 040_create_multiple.rs
> new file mode 100644
> index 000000000..372fad7ee
> --- /dev/null
> +++ b/rust/tests/040_create_multiple.rs
> @@ -0,0 +1,38 @@
> +/* libguestfs Rust bindings
> +Copyright (C) 2009-2019 Red Hat Inc.
> +
> +This program is free software; you can redistribute it and/or modify
> +it under the terms of the GNU General Public License as published by
> +the Free Software Foundation; either version 2 of the License, or
> +(at your option) any later version.
> +
> +This program is distributed in the hope that it will be useful,
> +but WITHOUT ANY WARRANTY; without even the implied warranty of
> +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> +GNU General Public License for more details.
> +
> +You should have received a copy of the GNU General Public License
> +along with this program; if not, write to the Free Software
> +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
> USA.
> +*/
> +
> +extern crate guestfs;
> +
> +fn create() -> guestfs::Handle {
> + match guestfs::Handle::create() {
> + Ok(g) => g,
> + Err(e) => panic!("fail: {}", e),
> + }
> +}
> +
> +fn ignore(_x: guestfs::Handle, _y: guestfs::Handle, _z: guestfs::Handle) {
> + // drop
> +}
> +
> +#[test]
> +fn create_multiple() {
> + let x = create();
> + let y = create();
> + let z = create();
> + ignore(x, y, z)
> +}
> diff --git a/rust/tests/050_handle_properties.rs b/rust/tests/
> 050_handle_properties.rs
> new file mode 100644
> index 000000000..0b955d5cf
> --- /dev/null
> +++ b/rust/tests/050_handle_properties.rs
> @@ -0,0 +1,62 @@
> +/* libguestfs Rust bindings
> +Copyright (C) 2009-2019 Red Hat Inc.
> +
> +This program is free software; you can redistribute it and/or modify
> +it under the terms of the GNU General Public License as published by
> +the Free Software Foundation; either version 2 of the License, or
> +(at your option) any later version.
> +
> +This program is distributed in the hope that it will be useful,
> +but WITHOUT ANY WARRANTY; without even the implied warranty of
> +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> +GNU General Public License for more details.
> +
> +You should have received a copy of the GNU General Public License
> +along with this program; if not, write to the Free Software
> +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
> USA.
> +*/
> +
> +extern crate guestfs;
> +
> +use std::default::Default;
> +
> +#[test]
> +fn verbose() {
> + let g = guestfs::Handle::create().expect("create");
> + g.set_verbose(true).expect("set_verbose");
> + assert_eq!(g.get_verbose().expect("get_verbose"), true);
> + g.set_verbose(false).expect("set_verbose");
> + assert_eq!(g.get_verbose().expect("get_verbose"), false);
> +}
> +
> +#[test]
> +fn trace() {
> + let g = guestfs::Handle::create().expect("create");
> + g.set_trace(true).expect("set_trace");
> + assert_eq!(g.get_trace().expect("get_trace"), true);
> + g.set_trace(false).expect("set_trace");
> + assert_eq!(g.get_trace().expect("get_trace"), false);
> +}
> +
> +#[test]
> +fn autosync() {
> + let g = guestfs::Handle::create().expect("create");
> + g.set_autosync(true).expect("set_autosync");
> + assert_eq!(g.get_autosync().expect("get_autosync"), true);
> + g.set_autosync(false).expect("set_autosync");
> + assert_eq!(g.get_autosync().expect("get_autosync"), false);
> +}
> +
> +#[test]
> +fn path() {
> + let g = guestfs::Handle::create().expect("create");
> + g.set_path(Some(".")).expect("set_path");
> + assert_eq!(g.get_path().expect("get_path"), ".");
> +}
> +
> +#[test]
> +fn add_drive() {
> + let g = guestfs::Handle::create().expect("create");
> + g.add_drive("/dev/null", Default::default())
> + .expect("add_drive");
> +}
> diff --git a/rust/tests/070_opt_args.rs b/rust/tests/070_opt_args.rs
> new file mode 100644
> index 000000000..04b4890c2
> --- /dev/null
> +++ b/rust/tests/070_opt_args.rs
> @@ -0,0 +1,41 @@
> +/* libguestfs Rust bindings
> +Copyright (C) 2009-2019 Red Hat Inc.
> +
> +This program is free software; you can redistribute it and/or modify
> +it under the terms of the GNU General Public License as published by
> +the Free Software Foundation; either version 2 of the License, or
> +(at your option) any later version.
> +
> +This program is distributed in the hope that it will be useful,
> +but WITHOUT ANY WARRANTY; without even the implied warranty of
> +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> +GNU General Public License for more details.
> +
> +You should have received a copy of the GNU General Public License
> +along with this program; if not, write to the Free Software
> +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
> USA.
> +*/
> +
> +extern crate guestfs;
> +
> +use std::default::Default;
> +
> +#[test]
> +fn no_optargs() {
> + let g = guestfs::Handle::create().expect("create");
> + g.add_drive("/dev/null", Default::default())
> + .expect("add_drive");
> +}
> +
> +#[test]
> +fn one_optarg() {
> + let g = guestfs::Handle::create().expect("create");
> + g.add_drive(
> + "/dev/null",
> + guestfs::AddDriveOptArgs {
> + readonly: Some(true),
> + ..Default::default()
> + },
> + )
> + .expect("add_drive");
> +}
> diff --git a/rust/tests/080_version.rs b/rust/tests/080_version.rs
> new file mode 100644
> index 000000000..19e441d67
> --- /dev/null
> +++ b/rust/tests/080_version.rs
> @@ -0,0 +1,26 @@
> +/* libguestfs Rust bindings
> +Copyright (C) 2009-2019 Red Hat Inc.
> +
> +This program is free software; you can redistribute it and/or modify
> +it under the terms of the GNU General Public License as published by
> +the Free Software Foundation; either version 2 of the License, or
> +(at your option) any later version.
> +
> +This program is distributed in the hope that it will be useful,
> +but WITHOUT ANY WARRANTY; without even the implied warranty of
> +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> +GNU General Public License for more details.
> +
> +You should have received a copy of the GNU General Public License
> +along with this program; if not, write to the Free Software
> +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
> USA.
> +*/
> +
> +extern crate guestfs;
> +
> +#[test]
> +fn version() {
> + let g = guestfs::Handle::create().expect("create");
> + let v = g.version().expect("version");
> + assert_eq!(v.major, 1)
> +}
> diff --git a/rust/tests/090_ret_values.rs b/rust/tests/090_ret_values.rs
> new file mode 100644
> index 000000000..d3e2e80da
> --- /dev/null
> +++ b/rust/tests/090_ret_values.rs
> @@ -0,0 +1,61 @@
> +/* libguestfs Rust bindings
> +Copyright (C) 2009-2019 Red Hat Inc.
> +
> +This program is free software; you can redistribute it and/or modify
> +it under the terms of the GNU General Public License as published by
> +the Free Software Foundation; either version 2 of the License, or
> +(at your option) any later version.
> +
> +This program is distributed in the hope that it will be useful,
> +but WITHOUT ANY WARRANTY; without even the implied warranty of
> +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> +GNU General Public License for more details.
> +
> +You should have received a copy of the GNU General Public License
> +along with this program; if not, write to the Free Software
> +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
> USA.
> +*/
> +
> +extern crate guestfs;
> +
> +#[test]
> +fn rint() {
> + let g = guestfs::Handle::create().expect("create");
> + assert_eq!(g.internal_test_rint("10").unwrap(), 10);
> + assert!(g.internal_test_rinterr().is_err())
> +}
> +
> +#[test]
> +fn rint64() {
> + let g = guestfs::Handle::create().expect("create");
> + assert_eq!(g.internal_test_rint64("10").unwrap(), 10);
> + assert!(g.internal_test_rint64err().is_err())
> +}
> +
> +#[test]
> +fn rbool() {
> + let g = guestfs::Handle::create().expect("create");
> + assert!(g.internal_test_rbool("true").unwrap());
> + assert!(!g.internal_test_rbool("false").unwrap());
> + assert!(g.internal_test_rboolerr().is_err())
> +}
> +
> +#[test]
> +fn rconststring() {
> + let g = guestfs::Handle::create().expect("create");
> + assert_eq!(
> + g.internal_test_rconststring("test").unwrap(),
> + "static string"
> + );
> + assert!(g.internal_test_rconststringerr().is_err())
> +}
> +
> +#[test]
> +fn rconstoptstring() {
> + let g = guestfs::Handle::create().expect("create");
> + assert_eq!(
> + g.internal_test_rconstoptstring("test").unwrap(),
> + Some("static string")
> + );
> + assert_eq!(g.internal_test_rconstoptstringerr().unwrap(), None)
> +}
> diff --git a/rust/tests/100_launch.rs b/rust/tests/100_launch.rs
> new file mode 100644
> index 000000000..1c1d8146a
> --- /dev/null
> +++ b/rust/tests/100_launch.rs
> @@ -0,0 +1,65 @@
> +/* libguestfs Rust bindings
> +Copyright (C) 2009-2019 Red Hat Inc.
> +
> +This program is free software; you can redistribute it and/or modify
> +it under the terms of the GNU General Public License as published by
> +the Free Software Foundation; either version 2 of the License, or
> +(at your option) any later version.
> +
> +This program is distributed in the hope that it will be useful,
> +but WITHOUT ANY WARRANTY; without even the implied warranty of
> +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> +GNU General Public License for more details.
> +
> +You should have received a copy of the GNU General Public License
> +along with this program; if not, write to the Free Software
> +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
> USA.
> +*/
> +
> +extern crate guestfs;
> +
> +use std::default::Default;
> +
> +#[test]
> +fn launch() {
> + let g = guestfs::Handle::create().expect("create");
> + g.add_drive_scratch(500 * 1024 * 1024, Default::default())
> + .expect("add_drive_scratch");
> + g.launch().expect("launch");
> + g.pvcreate("/dev/sda").expect("pvcreate");
> + g.vgcreate("VG", &["/dev/sda"]).expect("vgcreate");
> + g.lvcreate("LV1", "VG", 200).expect("lvcreate");
> + g.lvcreate("LV2", "VG", 200).expect("lvcreate");
> +
> + let lvs = g.lvs().expect("lvs");
> + assert_eq!(
> + lvs,
> + vec!["/dev/VG/LV1".to_string(), "/dev/VG/LV2".to_string()]
> + );
> +
> + g.mkfs("ext2", "/dev/VG/LV1", Default::default())
> + .expect("mkfs");
> + g.mount("/dev/VG/LV1", "/").expect("mount");
> + g.mkdir("/p").expect("mkdir");
> + g.touch("/q").expect("touch");
> +
> + let mut dirs = g.readdir("/").expect("readdir");
> +
> + dirs.sort_by(|a, b| a.name.cmp(&b.name));
> +
> + let mut v = Vec::new();
> + for x in &dirs {
> + v.push((x.name.as_str(), x.ftyp as u8));
> + }
> + assert_eq!(
> + v,
> + vec![
> + (".", b'd'),
> + ("..", b'd'),
> + ("lost+found", b'd'),
> + ("p", b'd'),
> + ("q", b'r')
> + ]
> + );
> + g.shutdown().expect("shutdown");
> +}
> --
> 2.20.1 (Apple Git-117)
>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://listman.redhat.com/archives/libguestfs/attachments/20190723/19ab628e/attachment.htm>
More information about the Libguestfs
mailing list