For closures with `cbkind = CBOnceCommand` an `FnOnce` instead of an
`FnMut` is required. (See
[
here](https://doc.rust-lang.org/std/ops/trait.FnOnce.html) for more
information about `FnOnce.) This makes the API more ergonomic since the
user can provide a closure which may only be called once instead of a
closure that should be possible to call any number of times.
---
generator/Rust.ml | 35 ++++++++++++++++++++++++++---------
rust/src/error.rs | 13 ++++++++++---
rust/src/handle.rs | 2 ++
rust/src/types.rs | 2 ++
4 files changed, 40 insertions(+), 12 deletions(-)
diff --git a/generator/Rust.ml b/generator/Rust.ml
index d0d1562..37582ac 100644
--- a/generator/Rust.ml
+++ b/generator/Rust.ml
@@ -133,15 +133,20 @@ let rec rust_arg_type : arg -> string = function
| BytesOut _ -> "&mut [u8]"
| BytesPersistIn _ -> "&'static [u8]"
| BytesPersistOut _ -> "&'static mut [u8]"
- | Closure { cbargs } -> "impl " ^ rust_closure_trait cbargs
+ | Closure { cbargs; cbkind } -> "impl " ^ rust_closure_trait cbargs
cbkind
(* Get the Rust closure trait for a callback, That is `Fn*(...) -> ...)`. *)
-and rust_closure_trait ?(lifetime = Some "'static") cbargs : string =
+and rust_closure_trait ?(lifetime = Some "'static") cbargs cbkind : string
=
let rust_cbargs = String.concat ", " (List.map rust_cbarg_type cbargs)
+ and closure_type =
+ match cbkind with
+ | CBOnceCommand -> "FnOnce"
+ | CBManyCommand | CBManyHandle -> "FnMut"
and lifetime_constraint =
match lifetime with None -> "" | Some x -> " + " ^ x
in
- "FnMut(" ^ rust_cbargs ^ ") -> c_int + Send + Sync" ^
lifetime_constraint
+ closure_type ^ "(" ^ rust_cbargs ^ ") -> c_int + Send + Sync"
+ ^ lifetime_constraint
(* Get the Rust type for a callback argument. *)
and rust_cbarg_type : cbarg -> string = function
@@ -448,8 +453,8 @@ let ffi_ret_to_rust (call : call) =
closure data, and a free function for the closure data. This struct is what
will be sent to a C function taking the closure as an argument. In fact,
the struct itself is generated by rust-bindgen. *)
-let print_rust_closure_to_raw_fn ({ cbname; cbargs } : closure) =
- let closure_trait = rust_closure_trait cbargs ~lifetime:None in
+let print_rust_closure_to_raw_fn ({ cbname; cbargs; cbkind } : closure) =
+ let closure_trait = rust_closure_trait cbargs cbkind ~lifetime:None in
let ffi_cbargs_names = List.flatten (List.map ffi_cbarg_names cbargs) in
let ffi_cbargs_types = List.flatten (List.map ffi_cbarg_types cbargs) in
let rust_cbargs_names = List.map rust_cbarg_name cbargs in
@@ -466,16 +471,28 @@ let print_rust_closure_to_raw_fn ({ cbname; cbargs } : closure) =
ffi_cbargs_names ffi_cbargs_types));
pr " where F: %s\n" closure_trait;
pr " {\n";
- pr " let callback_ptr = data as *mut F;\n";
- pr " let callback = &mut *callback_ptr;\n";
+ (match cbkind with
+ | CBManyCommand | CBManyHandle ->
+ pr " let callback_ptr = data as *mut F;\n";
+ pr " let callback = &mut *callback_ptr;\n"
+ | CBOnceCommand ->
+ pr " let callback_ptr = data as *mut Option<F>;\n";
+ pr " let callback_option: &mut Option<F> = &mut
*callback_ptr;\n";
+ pr " let callback: F = callback_option.take().unwrap();\n");
List.iter ffi_cbargs_to_rust cbargs;
pr " callback(%s)\n" (String.concat ", "
rust_cbargs_names);
pr " }\n";
- pr " let callback_data = Box::into_raw(Box::new(f));\n";
+ pr " let callback_data = Box::into_raw(Box::new(%s));\n"
+ (match cbkind with
+ | CBManyCommand | CBManyHandle -> "f"
+ | CBOnceCommand -> "Some(f)");
pr " sys::nbd_%s_callback {\n" cbname;
pr " callback: Some(call_closure::<F>),\n";
pr " user_data: callback_data as *mut _,\n";
- pr " free: Some(utils::drop_data::<F>),\n";
+ pr " free: Some(utils::drop_data::<%s>),\n"
+ (match cbkind with
+ | CBManyCommand | CBManyHandle -> "F"
+ | CBOnceCommand -> "Option<F>");
pr " }\n";
pr "}\n";
pr "\n"
diff --git a/rust/src/error.rs b/rust/src/error.rs
index 615a178..337d499 100644
--- a/rust/src/error.rs
+++ b/rust/src/error.rs
@@ -23,12 +23,16 @@ use std::io;
/// A general error type for libnbd.
#[derive(Debug, thiserror::Error)]
pub enum Error {
+ /// Non fatal errors used when a command failed but the handle is not dead.
#[error(transparent)]
Recoverable(ErrorKind),
+ /// A fatal error. After such an error, the handle is dead and there is no
+ /// point in issuing further commands.
#[error("Fatal: NBD handle is dead: {0}")]
Fatal(FatalErrorKind),
}
+/// An error kind for a Libnbd related error.
#[derive(Debug, thiserror::Error)]
pub enum ErrorKind {
#[error("Errno: {errno}: {description}")]
@@ -39,10 +43,13 @@ pub enum ErrorKind {
Errno(#[from] Errno),
}
+/// The kind of a fatal error.
#[derive(Debug, thiserror::Error)]
pub enum FatalErrorKind {
+ /// A Libnbd related error.
#[error(transparent)]
- Kind(#[from] ErrorKind),
+ Libnbd(#[from] ErrorKind),
+ /// Some other io error.
#[error(transparent)]
Io(#[from] io::Error),
}
@@ -87,7 +94,7 @@ impl Error {
pub(crate) unsafe fn get_error(handle: *mut sys::nbd_handle) -> Self {
let kind = ErrorKind::get_error();
if sys::nbd_aio_is_dead(handle) != 0 {
- Self::Fatal(FatalErrorKind::Kind(kind))
+ Self::Fatal(FatalErrorKind::Libnbd(kind))
} else {
Self::Recoverable(kind)
}
@@ -96,7 +103,7 @@ impl Error {
/// Get the errno value if any.
pub fn errno(&self) -> Option<i32> {
match self {
- Self::Recoverable(e) | Self::Fatal(FatalErrorKind::Kind(e)) => {
+ Self::Recoverable(e) | Self::Fatal(FatalErrorKind::Libnbd(e)) => {
e.errno()
}
Self::Fatal(FatalErrorKind::Io(e)) => e.raw_os_error(),
diff --git a/rust/src/handle.rs b/rust/src/handle.rs
index 477faa4..9e5e7d8 100644
--- a/rust/src/handle.rs
+++ b/rust/src/handle.rs
@@ -31,6 +31,8 @@ impl Handle {
if handle.is_null() {
Err(unsafe { Error::Fatal(ErrorKind::get_error().into()) })
} else {
+ // Set a debug callback communicating with any logging
+ // implementation as defined by the log crate.
#[allow(unused_mut)]
let mut nbd = Handle { handle };
#[cfg(feature = "log")]
diff --git a/rust/src/types.rs b/rust/src/types.rs
index eb2df06..af62140 100644
--- a/rust/src/types.rs
+++ b/rust/src/types.rs
@@ -15,4 +15,6 @@
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+/// A cookie is a 64 bit integer returned by some aio_* methods on
+/// [crate::Handle] used to identify a running command.
pub struct Cookie(pub(crate) u64);
--
2.41.0