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 _______________________________________________ Libguestfs mailing list Libguestfs@redhat.com https://listman.redhat.com/mailman/listinfo/libguestfs