[Libguestfs] Outline Rust bindings

Richard W.M. Jones rjones at redhat.com
Wed Oct 21 18:25:22 UTC 2015


Outline Rust bindings, obviously incomplete.

Attached is an example program showing how these bindings might be
used.  The most obvious thing is all the explicit error checking that
the caller must do (same as in C).  I'm not sure how natural that is,
but the alternatives seem to be lots of calls to either 'assert!'  or
'panic!' functions.

To try it out:

(1) Apply the patch.

(2) Copy attached main.rc into rust/src/

(3) Build libguestfs.

(4) Do:

  cd rust
  cargo build
  cargo run

Rich.

-- 
Richard Jones, Virtualization Group, Red Hat http://people.redhat.com/~rjones
Read my programming and virtualization blog: http://rwmj.wordpress.com
virt-top is 'top' for virtual machines.  Tiny program with many
powerful monitoring features, net stats, disk stats, logging, etc.
http://people.redhat.com/~rjones/virt-top
-------------- next part --------------
>From 0d20c4cd49f0f884020a4b41651241449896cd6d Mon Sep 17 00:00:00 2001
From: "Richard W.M. Jones" <rjones at redhat.com>
Date: Wed, 21 Oct 2015 13:48:39 +0100
Subject: [PATCH] Add Rust (language) bindings.

On Fedora you will need to install the Rust copr from:
https://copr.fedoraproject.org/coprs/fabiand/rust-binary/
until the package is added to Fedora:
https://bugzilla.redhat.com/show_bug.cgi?id=915043
---
 .gitignore            |   5 ++
 Makefile.am           |   3 ++
 README                |   2 +
 configure.ac          |  17 ++++++
 generator/Makefile.am |   2 +
 generator/main.ml     |   3 ++
 generator/rust.ml     | 147 ++++++++++++++++++++++++++++++++++++++++++++++++++
 rust/Cargo.toml.in    |   9 ++++
 rust/Makefile.am      |  30 +++++++++++
 rust/src/.gitignore   |   0
 src/guestfs.pod       |   2 +
 11 files changed, 220 insertions(+)
 create mode 100644 generator/rust.ml
 create mode 100644 rust/Cargo.toml.in
 create mode 100644 rust/Makefile.am
 create mode 100644 rust/src/.gitignore

diff --git a/.gitignore b/.gitignore
index d5c5d1e..dcf9d63 100644
--- a/.gitignore
+++ b/.gitignore
@@ -468,6 +468,11 @@ Makefile.in
 /ruby/ext/guestfs/mkmf.log
 /ruby/Rakefile
 /ruby/stamp-rdoc
+/rust/Cargo.lock
+/rust/Cargo.toml
+/rust/src/ffi.rs
+/rust/src/lib.rs
+/rust/target
 /run
 /sparsify/.depend
 /sparsify/stamp-virt-sparsify.pod
diff --git a/Makefile.am b/Makefile.am
index 713df42..aa96fc3 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -123,6 +123,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/README b/README
index 19a1fb2..0669806 100644
--- a/README
+++ b/README
@@ -223,6 +223,8 @@ The full requirements are described below.
 +--------------+-------------+---+-----------------------------------------+
 | golang       | 1.1.1       | O | For the Go bindings.                    |
 +--------------+-------------+---+-----------------------------------------+
+| rustc        | 1.0         | O | For the Rust bindings.                  |
++--------------+-------------+---+-----------------------------------------+
 | valgrind     |             | O | For testing for memory problems.        |
 +--------------+-------------+---+-----------------------------------------+
 | Sys::Virt    |             | O | Perl bindings for libvirt.              |
diff --git a/configure.ac b/configure.ac
index 4f6650e..e5b8ade 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1578,6 +1578,21 @@ AS_IF([test "x$enable_golang" != "xno"],[
 ],[GOLANG=no])
 AM_CONDITIONAL([HAVE_GOLANG],[test "x$GOLANG" != "xno"])
 
+dnl Rust compiler.
+AC_ARG_ENABLE([rust],
+    AS_HELP_STRING([--disable-rust], [disable Rust language bindings]),
+        [],
+        [enable_rust=yes])
+AS_IF([test "x$enable_rust" != "xno"],[
+    AC_CHECK_PROG([RUSTC],[rustc],[rustc],[no])
+    AC_CHECK_PROG([CARGO],[cargo],[cargo],[no])
+],[
+    RUSTC=no
+    CARGO=no
+])
+AM_CONDITIONAL([HAVE_RUST],
+               [test "x$RUSTC" != "xno" && test "x$CARGO" != "xno"])
+
 dnl Check for Perl modules needed by Perl virt tools (virt-df, etc.)
 AS_IF([test "x$PERL" != "xno"],[
     missing_perl_modules=no
@@ -1791,6 +1806,8 @@ AC_CONFIG_FILES([Makefile
                  ruby/Rakefile
                  ruby/examples/Makefile
                  ruby/ext/guestfs/extconf.rb
+                 rust/Cargo.toml
+                 rust/Makefile
                  sparsify/Makefile
                  src/Makefile
                  src/libguestfs.pc
diff --git a/generator/Makefile.am b/generator/Makefile.am
index a3fe50d..34ac5c5 100644
--- a/generator/Makefile.am
+++ b/generator/Makefile.am
@@ -48,6 +48,7 @@ sources = \
 	prepopts.mli \
 	python.ml \
 	ruby.ml \
+	rust.ml \
 	structs.ml \
 	structs.mli \
 	tests_c_api.ml \
@@ -85,6 +86,7 @@ objects = \
 	lua.cmo \
 	gobject.cmo \
 	golang.cmo \
+	rust.cmo \
 	bindtests.cmo \
 	errnostring.cmo \
 	customize.cmo \
diff --git a/generator/main.ml b/generator/main.ml
index 1e0e7d6..d56f028 100644
--- a/generator/main.ml
+++ b/generator/main.ml
@@ -43,6 +43,7 @@ open Erlang
 open Lua
 open Gobject
 open Golang
+open Rust
 open Bindtests
 open Errnostring
 open Customize
@@ -165,6 +166,8 @@ Run it from the top source directory using the command
   output_to "lua/bindtests.lua" generate_lua_bindtests;
   output_to "golang/src/libguestfs.org/guestfs/guestfs.go" generate_golang_go;
   output_to "golang/bindtests.go" generate_golang_bindtests;
+  output_to "rust/src/ffi.rs" generate_rust_ffi_rs;
+  output_to "rust/src/lib.rs" generate_rust_lib_rs;
 
   output_to "gobject/bindtests.js" generate_gobject_js_bindtests;
   output_to "gobject/Makefile.inc" generate_gobject_makefile;
diff --git a/generator/rust.ml b/generator/rust.ml
new file mode 100644
index 0000000..8678e91
--- /dev/null
+++ b/generator/rust.ml
@@ -0,0 +1,147 @@
+(* libguestfs
+ * Copyright (C) 2015 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 Printf
+
+open Types
+open Utils
+open Pr
+open Docstrings
+open Optgroups
+open Actions
+open Structs
+open C
+open Events
+
+(* Generate rust bindings. *)
+
+let generate_rust_ffi_rs () =
+  generate_header CPlusPlusStyle LGPLv2plus;
+
+  pr "\
+use libc::{c_char, c_uint, int64_t};
+
+// Represents the opaque handle (guestfs_h).
+#[allow(non_camel_case_types)]
+pub enum guestfs_h {}
+
+// XXX generate
+#[repr(C)]
+struct _version {
+    pub _major: int64_t,
+    pub _minor: int64_t,
+    pub _release: int64_t,
+    pub _extra: *const c_char,
+}
+
+#[link (name = \"guestfs\")]
+extern {
+    pub fn guestfs_create () -> *mut guestfs_h;
+    pub fn guestfs_create_flags (flags: c_uint, ...) -> *mut guestfs_h;
+    pub fn guestfs_close (g: *mut guestfs_h);
+
+    pub fn guestfs_last_error (g: *mut guestfs_h) -> *const c_char;
+
+    // XXX generate these
+    pub fn guestfs_version (g: *mut guestfs_h) -> *mut _version;
+    pub fn guestfs_free_version (version: *mut _version);
+}
+
+"
+
+let generate_rust_lib_rs () =
+  generate_header CPlusPlusStyle LGPLv2plus;
+
+  pr "\
+extern crate libc;
+
+use std::ffi::CStr;
+use std::str;
+use std::sync::Arc;
+
+// Temporarily allow dead_code in the ffi submodule.  Eventually
+// we can remove this once every ffi function has a safe wrapper. XXX
+#[allow(dead_code)]
+mod ffi;
+
+struct GuestfsHandleInternal {
+    g: *mut ffi::guestfs_h,
+}
+
+impl Drop for GuestfsHandleInternal {
+    fn drop (&mut self) {
+        unsafe { ffi::guestfs_close (self.g); }
+    }
+}
+
+pub struct GuestfsHandle {
+    rc : Arc<GuestfsHandleInternal>,
+}
+
+// XXX generate
+pub struct Version {
+    pub major: i64,
+    pub minor: i64,
+    pub release: i64,
+    pub extra: String,
+}
+
+fn get_last_error (g: *mut ffi::guestfs_h) -> String {
+    let error_cstr = unsafe { ffi::guestfs_last_error (g) };
+    let error_buf = unsafe { CStr::from_ptr (error_cstr).to_bytes() };
+    let error = str::from_utf8 (error_buf).unwrap().to_owned();
+    return error;
+}
+
+impl GuestfsHandle {
+    pub fn create () -> Result<GuestfsHandle, String> {
+        let g = unsafe { ffi::guestfs_create () };
+        if g.is_null() {
+            //let errno = Error::last_os_error().raw_os_error();
+            // XXX convert errno to a string
+            return Err (\"XXX\".to_string());
+        }
+
+        let rc = Arc::new (GuestfsHandleInternal { g: g });
+        Ok (GuestfsHandle { rc: rc })
+    }
+
+    // XXX generate these
+    pub fn version (&mut self) -> Result<Version, String> {
+        let v = unsafe { ffi::guestfs_version (self.rc.g) };
+        if v.is_null() {
+            return Err (get_last_error (self.rc.g));
+        }
+        let major = unsafe { (*v)._major };
+        let minor = unsafe { (*v)._minor };
+        let release = unsafe { (*v)._release };
+        let extra_cstr = unsafe { (*v)._extra };
+        let extra_buf = unsafe { CStr::from_ptr (extra_cstr).to_bytes() };
+        let extra = str::from_utf8 (extra_buf).unwrap().to_owned();
+        unsafe { ffi::guestfs_free_version (v) };
+        Ok (Version {
+            major: major,
+            minor: minor,
+            release: release,
+            extra: extra,
+        })
+    }
+}
+"
diff --git a/rust/Cargo.toml.in b/rust/Cargo.toml.in
new file mode 100644
index 0000000..11d113e
--- /dev/null
+++ b/rust/Cargo.toml.in
@@ -0,0 +1,9 @@
+[package]
+name = "guestfs"
+version = "@PACKAGE_VERSION@"
+authors = ["libguestfs authors <libguestfs at redhat.com>"]
+description = "Library for accessing and modifying virtual machine images"
+repository = "https://github.com/libguestfs/libguestfs"
+
+[dependencies]
+libc = "*"
diff --git a/rust/Makefile.am b/rust/Makefile.am
new file mode 100644
index 0000000..5c363f4
--- /dev/null
+++ b/rust/Makefile.am
@@ -0,0 +1,30 @@
+# libguestfs Rust bindings
+# Copyright (C) 2015 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/ffi.rs \
+	src/lib.rs
+
+EXTRA_DIST = \
+	$(generator_built)
+
+if HAVE_RUST
+
+
+endif
diff --git a/rust/src/.gitignore b/rust/src/.gitignore
new file mode 100644
index 0000000..e69de29
diff --git a/src/guestfs.pod b/src/guestfs.pod
index f8d7e2c..31b0ddf 100644
--- a/src/guestfs.pod
+++ b/src/guestfs.pod
@@ -4606,6 +4606,8 @@ L<virt-v2v(1)> command and documentation.
 
 =item F<ruby>
 
+=item F<rust>
+
 Language bindings.
 
 =back
-- 
2.5.0

-------------- next part --------------
A non-text attachment was scrubbed...
Name: main.rs
Type: application/rls-services+xml
Size: 419 bytes
Desc: not available
URL: <http://listman.redhat.com/archives/libguestfs/attachments/20151021/d409c76a/attachment.rs>


More information about the Libguestfs mailing list