Rust bindings: Add Rust bindings

This patch includes Actions and their tests. Missing:

- Events
- Examples

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
This commit is contained in:
Hiroyuki_Katsura
2019-07-29 12:10:42 +09:00
committed by Richard W.M. Jones
parent 6d251e3828
commit 3bbd00c83e
31 changed files with 1562 additions and 3 deletions

View File

@@ -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

View File

@@ -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."

View File

@@ -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 \

View File

@@ -983,6 +983,73 @@ and generate_php_bindtests () =
dump "bindtests"
and generate_rust_bindtests () =
let copywrites = ["Hiroyuki Katsura <hiroyuki.katsura.0513@gmail.com>"] in
generate_header ~copywrites:copywrites 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.
*)

View File

@@ -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

View File

@@ -76,7 +76,7 @@ type comment_style =
| ErlangStyle | LuaStyle | PODStyle
type license = GPLv2plus | LGPLv2plus
let generate_header ?(inputs = []) ?emacs_mode comment license =
let generate_header ?(copywrites = ["Red Hat Inc."]) ?(inputs = []) ?emacs_mode comment license =
let c = match comment with
| CStyle -> pr "/* "; " *"
| CPlusPlusStyle -> pr "// "; "//"
@@ -102,7 +102,9 @@ let generate_header ?(inputs = []) ?emacs_mode comment license =
);
pr "%s ANY CHANGES YOU MAKE TO THIS FILE WILL BE LOST.\n" c;
pr "%s\n" c;
pr "%s Copyright (C) %s Red Hat Inc.\n" c copyright_years;
List.iter (fun x ->
pr "%s Copyright (C) %s %s\n" c copyright_years x;
) copywrites;
pr "%s\n" c;
(match license with
| GPLv2plus ->

View File

@@ -31,4 +31,4 @@ val version_added : Types.action -> string option
val copyright_years : string
val generate_header : ?inputs:string list -> ?emacs_mode:string -> comment_style -> license -> unit
val generate_header : ?copywrites:string list -> ?inputs:string list -> ?emacs_mode:string -> comment_style -> license -> unit

View File

@@ -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

558
generator/rust.ml Normal file
View File

@@ -0,0 +1,558 @@
(* libguestfs
* Copyright (C) 2019 Hiroyuki Katsura <hiroyuki.katsura.0513@gmail.com>
*
* 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 copywrites = ["Hiroyuki Katsura <hiroyuki.katsura.0513@gmail.com>"]
(* Utilities for Rust *)
(* Are there corresponding functions to them? *)
(* Should they be placed in utils.ml? *)
let rec indent = function
| x when x > 0 -> pr " "; indent (x - 1)
| _ -> ()
let snake2caml name =
let l = String.nsplit "_" name in
let l = List.map String.capitalize_ascii 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.mem s black_list then
s ^ "_"
else
s
let generate_rust () =
generate_header ~copywrites:copywrites 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: char_ptr_to_string((*raw).%s)?,\n" n 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 contains_ptr =
List.exists (
function
| OString _
| OStringList _ -> true
| _ -> false
)
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 { char_ptr_to_string(r) };\n";
pr3 "unsafe { free(r as *const c_void) };";
pr3 "s?\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"

22
generator/rust.mli Normal file
View File

@@ -0,0 +1,22 @@
(* libguestfs
* Copyright (C) 2019 Hiroyuki Katsura <hiroyuki.katsura.0513@gmail.com>
*
* 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

33
m4/guestfs-rust.m4 Normal file
View File

@@ -0,0 +1,33 @@
# libguestfs
# Copyright (C) 2019 Hiroyuki Katsura <hiroyuki.katsura.0513@gmail.com>
#
# 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"])

10
run.in
View File

@@ -201,6 +201,16 @@ else
fi
export CGO_LDFLAGS
# For rust
export RUST="@RUST@"
export CARGO="@CARGO@"
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"

3
rust/.gitignore vendored Normal file
View File

@@ -0,0 +1,3 @@
/target
**/*.rs.bk
Cargo.lock

23
rust/Cargo.toml.in Normal file
View File

@@ -0,0 +1,23 @@
# libguestfs Rust tests
# Copyright (C) 2019 Hiroyuki Katsura <hiroyuki.katsura.0513@gmail.com>
#
# 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.
[package]
name = "guestfs"
version = "@VERSION@"
edition = "2018"
[dependencies]

45
rust/Makefile.am Normal file
View File

@@ -0,0 +1,45 @@
# libguestfs rust bindings
# Copyright (C) 2019 Hiroyuki Katsura <hiroyuki.katsura.0513@gmail.com>
#
# 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 \
src/*.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
TESTS_ENVIRONMENT = $(top_builddir)/run --test

23
rust/run-bindtests Executable file
View File

@@ -0,0 +1,23 @@
#!/bin/sh -
# libguestfs Rust bindings
# Copyright (C) 2019 Hiroyuki Katsura <hiroyuki.katsura.0513@gmail.com>
#
# 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

21
rust/run-tests Executable file
View File

@@ -0,0 +1,21 @@
#!/bin/sh -
# libguestfs Rust tests
# Copyright (C) 2019 Hiroyuki Katsura <hiroyuki.katsura.0513@gmail.com>
#
# 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

121
rust/src/base.rs Normal file
View File

@@ -0,0 +1,121 @@
/* libguestfs Rust bindings
* Copyright (C) 2019 Hiroyuki Katsura <hiroyuki.katsura.0513@gmail.com>
*
* 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;
#[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, error::Error> {
let g = unsafe { guestfs_create() };
if g.is_null() {
Err(error::Error::Create)
} else {
Ok(Handle { g })
}
}
pub fn create_flags(flags: CreateFlags) -> Result<Handle, error::Error> {
let g = unsafe { guestfs_create_flags(flags.to_libc_int()) };
if g.is_null() {
Err(error::Error::Create)
} 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
}
}

0
rust/src/bin/.gitkeep Normal file
View File

70
rust/src/error.rs Normal file
View File

@@ -0,0 +1,70 @@
/* libguestfs Rust bindings
* Copyright (C) 2019 Hiroyuki Katsura <hiroyuki.katsura.0513@gmail.com>
*
* 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 crate::utils;
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 base::guestfs_h) -> *const c_char;
fn guestfs_last_errno(g: *mut base::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),
Create,
}
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 base::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 { utils::char_ptr_to_string(c_msg).unwrap() };
let errno = unsafe { guestfs_last_errno(self.g) };
Error::API(APIError {
operation,
message,
errno,
})
}
}

26
rust/src/lib.rs Normal file
View File

@@ -0,0 +1,26 @@
/* libguestfs Rust bindings
* Copyright (C) 2019 Hiroyuki Katsura <hiroyuki.katsura.0513@gmail.com>
*
* 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
*/
mod base;
mod error;
mod guestfs;
mod utils;
pub use crate::base::*;
pub use crate::error::*;
pub use crate::guestfs::*;

146
rust/src/utils.rs Normal file
View File

@@ -0,0 +1,146 @@
/* libguestfs Rust bindings
* Copyright (C) 2019 Hiroyuki Katsura <hiroyuki.katsura.0513@gmail.com>
*
* 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};
use std::str;
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::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::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 { char_ptr_to_string(key) }?;
let val = unsafe { char_ptr_to_string(val) }?;
map.insert(key, val);
} 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::Error>>(
l: *const RawList<T>,
) -> Result<Vec<S>, error::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::Error> {
let mut v = Vec::new();
for x in NullTerminatedIter::new(l) {
let s = unsafe { char_ptr_to_string(x) }?;
v.push(s);
}
Ok(v)
}
pub(crate) unsafe fn char_ptr_to_string(ptr: *const c_char) -> Result<String, str::Utf8Error> {
fn char_ptr_to_string_inner(ptr: *const c_char) -> Result<String, str::Utf8Error> {
let s = unsafe { ffi::CStr::from_ptr(ptr) };
let s = s.to_str()?.to_string();
Ok(s)
}
char_ptr_to_string_inner(ptr)
}

24
rust/tests/010_load.rs Normal file
View File

@@ -0,0 +1,24 @@
/* libguestfs Rust bindings
* Copyright (C) 2019 Hiroyuki Katsura <hiroyuki.katsura.0513@gmail.com>
*
* 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
}

24
rust/tests/020_create.rs Normal file
View File

@@ -0,0 +1,24 @@
/* libguestfs Rust bindings
* Copyright (C) 2019 Hiroyuki Katsura <hiroyuki.katsura.0513@gmail.com>
*
* 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();
}

View File

@@ -0,0 +1,30 @@
/* libguestfs Rust bindings
* Copyright (C) 2019 Hiroyuki Katsura <hiroyuki.katsura.0513@gmail.com>
*
* 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
}

View File

@@ -0,0 +1,38 @@
/* libguestfs Rust bindings
* Copyright (C) 2019 Hiroyuki Katsura <hiroyuki.katsura.0513@gmail.com>
*
* 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)
}

View File

@@ -0,0 +1,62 @@
/* libguestfs Rust bindings
* Copyright (C) 2019 Hiroyuki Katsura <hiroyuki.katsura.0513@gmail.com>
*
* 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");
}

View File

@@ -0,0 +1,41 @@
/* libguestfs Rust bindings
* Copyright (C) 2019 Hiroyuki Katsura <hiroyuki.katsura.0513@gmail.com>
*
* 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");
}

26
rust/tests/080_version.rs Normal file
View File

@@ -0,0 +1,26 @@
/* libguestfs Rust bindings
* Copyright (C) 2019 Hiroyuki Katsura <hiroyuki.katsura.0513@gmail.com>
*
* 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)
}

View File

@@ -0,0 +1,61 @@
/* libguestfs Rust bindings
* Copyright (C) 2019 Hiroyuki Katsura <hiroyuki.katsura.0513@gmail.com>
*
* 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)
}

65
rust/tests/100_launch.rs Normal file
View File

@@ -0,0 +1,65 @@
/* libguestfs Rust bindings
* Copyright (C) 2019 Hiroyuki Katsura <hiroyuki.katsura.0513@gmail.com>
*
* 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");
}