diff --git a/Cargo.lock b/Cargo.lock index ff32633..9af7062 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -190,6 +190,7 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" name = "nixide" version = "0.1.0" dependencies = [ + "libc", "nixide-sys", "serial_test", "stdext", diff --git a/nixide/Cargo.toml b/nixide/Cargo.toml index e97d5fe..7a1b4e4 100644 --- a/nixide/Cargo.toml +++ b/nixide/Cargo.toml @@ -24,8 +24,9 @@ util = [] gc = [] [dependencies] -nixide-sys = { path = "../nixide-sys", version = "0.1.0" } +libc = "0.2.183" stdext = "0.3.3" +nixide-sys = { path = "../nixide-sys", version = "0.1.0" } [dev-dependencies] serial_test = "3.4.0" diff --git a/nixide/src/errors/error.rs b/nixide/src/errors/error.rs index bcebcf8..29696f1 100644 --- a/nixide/src/errors/error.rs +++ b/nixide/src/errors/error.rs @@ -137,15 +137,15 @@ impl Display for NixideError { } } -pub trait AsErr { - fn as_err(self) -> Result<(), T>; -} +// pub trait AsErr { +// fn as_err(self) -> Result<(), T>; +// } -impl AsErr for Option { - fn as_err(self) -> Result<(), NixideError> { - match self { - Some(err) => Err(err), - None => Ok(()), - } - } -} +// impl AsErr for Option { +// fn as_err(self) -> Result<(), NixideError> { +// match self { +// Some(err) => Err(err), +// None => Ok(()), +// } +// } +// } diff --git a/nixide/src/lib.rs b/nixide/src/lib.rs index 05a1738..5b363b4 100644 --- a/nixide/src/lib.rs +++ b/nixide/src/lib.rs @@ -1,5 +1,9 @@ // #![warn(missing_docs)] +// #[allow(unused_extern_crates)] +pub extern crate libc; +pub extern crate nixide_sys as sys; + pub(crate) mod errors; // mod expr; // mod flake; @@ -15,8 +19,6 @@ pub use store::{Store, StorePath}; pub use verbosity::NixVerbosity; pub use version::NixVersion; -pub use nixide_sys as sys; - /// Sets the verbosity level /// /// # Arguments diff --git a/nixide/src/store/mod.rs b/nixide/src/store/mod.rs index 7c057d7..0d52edb 100644 --- a/nixide/src/store/mod.rs +++ b/nixide/src/store/mod.rs @@ -23,7 +23,7 @@ use std::result::Result; use crate::errors::{new_nixide_error, ErrorContext}; use crate::stdext::CCharPtrExt; -use crate::util::wrap::{self, UserData}; +use crate::util::wrap; use crate::util::wrappers::AsInnerPtr; use crate::{NixideError, NixideResult}; use nixide_sys as sys; @@ -87,96 +87,42 @@ impl Store { pub fn realise( &self, path: &StorePath, - callback: fn(&str, &StorePath), - ) -> NixideResult<(String, StorePath)> { - // // Type alias for our userdata: (outputs vector, context) - // type Userdata = ( - // Vec<(String, StorePath)>, - // Arc, - // fn(&str, &StorePath), - // ); - // - // // Callback function that will be called for each realized output - // unsafe extern "C" fn realise_callback( - // userdata: *mut c_void, - // out_name_ptr: *const c_char, - // out_path_ptr: *const sys::StorePath, - // ) { - // // SAFETY: userdata is a valid pointer to our (Vec, Arc) tuple - // let (outputs, ctx, callback) = unsafe { &mut *(userdata as *mut Userdata) }; - // - // // SAFETY: outname is a valid C string from Nix - // let output_name = if !out_name_ptr.is_null() { - // unsafe { CStr::from_ptr(out_name_ptr).to_string_lossy().into_owned() } - // } else { - // String::from("out") // Default output name - // }; - // - // // SAFETY: out is a valid StorePath pointer from Nix, we need to clone it - // // because Nix owns the original and may free it after the callback - // if !out_path_ptr.is_null() { - // let cloned_path_ptr = - // unsafe { sys::nix_store_path_clone(out_path_ptr as *mut sys::StorePath) }; - // if let Some(inner) = NonNull::new(cloned_path_ptr) { - // let store_path = StorePath { inner }; - // - // callback(output_name.as_ref(), &store_path); - // - // outputs.push((output_name, store_path)); - // } - // } - // } - // - // // Create userdata with empty outputs vector and context - // let mut userdata: Userdata = (Vec::new(), Arc::new(ErrorContext::new()), callback); - // let userdata_ptr = &mut userdata as *mut Userdata as *mut std::os::raw::c_void; - // - // // SAFETY: All pointers are valid, callback is compatible with the FFI signature - // // - self._context is valid for the duration of this call - // // - self.inner is valid (checked in Store::open) - // // - path.inner is valid (checked in StorePath::parse) - // // - userdata_ptr points to valid stack memory - // // - realize_callback matches the expected C function signature - // let err = unsafe { - // sys::nix_store_realise( - // ctx.as_ptr(), - // self.inner.as_ptr(), - // path.as_ptr(), - // userdata_ptr, - // Some(realise_callback), - // ) - // }; - + user_callback: fn(&str, &StorePath), + ) -> NixideResult> { wrap::nix_callback!( |; userdata: fn(&str, &StorePath); output_name_ptr: *const c_char, output_path_ptr: *const sys::StorePath| - -> NixideResult<(String, StorePath)> { + -> Vec<(String, StorePath)> { // XXX: TODO: test to see if this is ever null ("out" as a default feels unsafe...) - // let output_name = output_name_ptr.to_utf8_string().unwrap_or("out".to_owned()); // NOTE: this also ensures `output_name_ptr` isn't null - let output_name = output_name_ptr.to_utf8_string()?; + let output_name = output_name_ptr.to_utf8_string().expect("IDK1"); let inner = wrap::nix_ptr_fn!(|ctx| unsafe { sys::nix_store_path_clone(output_path_ptr as *mut sys::StorePath) - })?; + }).expect("IDK2"); let store_path = StorePath { inner }; let callback = unsafe { (*userdata).inner }; callback(output_name.as_ref(), &store_path); - Ok((output_name, store_path)) + (output_name, store_path); }, |callback, - state: *mut UserData>, + state: *mut __UserData, ctx: &ErrorContext| unsafe { + // register userdata + // WARNING: Using `write` instead of assignment via `=` + // WARNING: to not call `drop` on the old, uninitialized value. + (&raw mut (*state).inner).write(user_callback); + sys::nix_store_realise( ctx.as_ptr(), self.inner.as_ptr(), path.as_ptr(), (*state).inner_ptr() as *mut c_void, Some(callback), - ) + ); } ) } diff --git a/nixide/src/store/tests.rs b/nixide/src/store/tests.rs index d0b7744..da72ae7 100644 --- a/nixide/src/store/tests.rs +++ b/nixide/src/store/tests.rs @@ -16,9 +16,6 @@ fn test_store_opening() { sys::nix_libstore_init(ctx.as_ptr()); ctx.pop() .expect("nix_libstore_init failed with bad ErrorContext"); - sys::nix_libexpr_init(ctx.as_ptr()); - ctx.pop() - .expect("nix_libexpr_init failed with bad ErrorContext"); }; let _store = Store::open(None).expect("Failed to open store"); @@ -35,9 +32,6 @@ fn test_store_path_parse() { sys::nix_libstore_init(ctx.as_ptr()); ctx.pop() .expect("nix_libstore_init failed with bad ErrorContext"); - sys::nix_libexpr_init(ctx.as_ptr()); - ctx.pop() - .expect("nix_libexpr_init failed with bad ErrorContext"); }; let store = Store::open(None).expect("Failed to open store"); @@ -58,9 +52,6 @@ fn test_store_path_clone() { sys::nix_libstore_init(ctx.as_ptr()); ctx.pop() .expect("nix_libstore_init failed with bad ErrorContext"); - sys::nix_libexpr_init(ctx.as_ptr()); - ctx.pop() - .expect("nix_libexpr_init failed with bad ErrorContext"); }; let store = Store::open(None).expect("Failed to open store"); diff --git a/nixide/src/util/panic.rs b/nixide/src/util/panic.rs index 51f2f34..b9e5e95 100644 --- a/nixide/src/util/panic.rs +++ b/nixide/src/util/panic.rs @@ -9,10 +9,10 @@ macro_rules! panic_issue { macro_rules! panic_issue_call_failed { () => {{ - crate::util::panic_issue!("[nixide] call to `{}` failed", stdext::debug_name!()) + crate::util::panic_issue!("[nixide] call to `{}` failed", $crate::stdext::debug_name!()) }}; ($($arg:expr),*) => {{ - crate::util::panic_issue!("[nixide] call to `{}` failed with \"{}\"", stdext::debug_name!(), format!($($arg),*)) + crate::util::panic_issue!("[nixide] call to `{}` failed with \"{}\"", $crate::stdext::debug_name!(), format!($($arg),*)) }}; } diff --git a/nixide/src/util/wrap.rs b/nixide/src/util/wrap.rs index 595511b..33790c9 100644 --- a/nixide/src/util/wrap.rs +++ b/nixide/src/util/wrap.rs @@ -4,6 +4,8 @@ pub(crate) struct UserData { pub inner: S, pub retval: T, + // XXX: TODO: write impl functions to set and get these values, + // XXX: TODO: and another one to unwrap a `MaybeUninit>` #[cfg(debug_assertions)] pub init_inner: bool, @@ -18,14 +20,6 @@ impl AsMut> for UserData { } impl UserData { - /// # Warning - /// - /// Ensure `self.retval` has been initialised before unwrapping! - /// - // pub unsafe fn unwrap(self) -> (S, T) { - // (self.inner, self.retval) - // } - pub unsafe fn as_mut_ptr(&mut self) -> *mut Self { self as *mut Self } @@ -36,10 +30,6 @@ impl UserData { &raw mut (*ptr).inner } } - - // pub unsafe fn retval_ptr(&mut self) -> *mut c_void { - // &mut self.retval as *mut T as *mut c_void - // } } macro_rules! nonnull { @@ -73,42 +63,27 @@ macro_rules! nix_ptr_fn { } pub(crate) use nix_ptr_fn; -macro_rules! __nix_callback { - ($userdata_type:ty, $ret:ty, $callback:expr) => {{ - let mut __ctx = $crate::errors::ErrorContext::new(); - let mut __state: ::std::mem::MaybeUninit<__UserData> = ::std::mem::MaybeUninit::uninit(); - - $callback(__wrapper_callback, __state.as_mut_ptr(), &__ctx); - - // add type annotations for compiler - let __return: $ret = __ctx - .pop() - .and_then(|_| unsafe { __state.assume_init().retval }); - __return - }}; -} -#[allow(unused_imports)] // XXX: TODO: replace the tail of `nix_callback!` with this macro -pub(crate) use __nix_callback; - /// `libnix` functions consistently either expect the `userdata`/`user_data` (inconsistently named in the API...) /// field to be the first or last parameter (differs between function). The `nix_callback!` macro allows the /// position to be specified by either the following syntax: /// /// ```rs -/// nix_callback(userdata; ...); // first parameter -/// nix_callback(...; userdata); // last parameter +/// nix_callback!(; userdata; ...); // first parameter +/// nix_callback!(...; userdata; ); // last parameter /// ``` /// macro_rules! nix_callback { ( | $( $($pre:ident : $pre_ty:ty),+ $(,)? )? ; $userdata:ident : $userdata_type:ty ; $( $($post:ident : $post_ty:ty),+ $(,)? )? | -> $ret:ty $body:block, $function:expr $(,)? ) => {{ type __UserData = $crate::util::wrap::UserData<$userdata_type, $ret>; // create a function item that wraps the closure body (so it has a concrete type) + // WARNING: this function must have no return type, use the `UserData.inner` + // WARNING: field instead as an `out` pointer. #[allow(unused_variables)] unsafe extern "C" fn __captured_fn( $($( $pre: $pre_ty, )*)? $userdata: *mut __UserData, $($( $post: $post_ty, )*)? - ) -> $ret { $body } + ) { $body } unsafe extern "C" fn __wrapper_callback( $($( $pre: $pre_ty, )*)? @@ -116,17 +91,12 @@ macro_rules! nix_callback { $($( $post: $post_ty, )*)? ) { unsafe { - let userdata_ = $userdata as *mut __UserData; - let stored_retval = &raw mut (*userdata_).retval; - - let retval = - __captured_fn( - $($( $pre, )*)? - userdata_, - $($( $post, )*)? - ); - - stored_retval.write(retval) + __captured_fn( + $($( $pre, )*)? + // userdata_, + $userdata as *mut __UserData, + $($( $post, )*)? + ); } } @@ -136,21 +106,35 @@ macro_rules! nix_callback { $function(__wrapper_callback, __state.as_mut_ptr(), &__ctx); // type annotations for compiler - let __result: $ret = __ctx.pop().and_then(|_| unsafe { __state.assume_init().retval }); - __result + let __return: $crate::NixideResult<$ret> = __ctx.pop().and_then(|_| ::std::result::Result::Ok(unsafe { __state.assume_init().retval })); + __return }}; } pub(crate) use nix_callback; -// XXX: TODO: convert these to declarative macros macro_rules! nix_string_callback { ($function:expr $(,)?) => {{ - $crate::util::wrap::nix_callback!( - |start: *const ::std::ffi::c_char, n: ::std::ffi::c_uint; userdata: ();| -> $crate::NixideResult { - $crate::stdext::CCharPtrExt::to_utf8_string_n(start, n as usize) + #[repr(C)] + struct __ReturnType { + start: *const ::std::ffi::c_char, + n: ::std::ffi::c_uint, + } + + let __result = $crate::util::wrap::nix_callback!( + |start: *const ::std::ffi::c_char, n: ::std::ffi::c_uint; userdata: ();| -> (*const ::std::ffi::c_char, ::std::ffi::c_uint) { + unsafe { + let retval = &raw mut (*userdata).retval; + retval.write((start, n)) + } }, $function - ) + ); + + __result.and_then(|(start, n)| { + let __return = $crate::stdext::CCharPtrExt::to_utf8_string_n(start, n as usize); + + __return + }) }}; } pub(crate) use nix_string_callback; diff --git a/nixide/src/util/wrappers.rs b/nixide/src/util/wrappers.rs index 829437a..ab43cce 100644 --- a/nixide/src/util/wrappers.rs +++ b/nixide/src/util/wrappers.rs @@ -1,17 +1,23 @@ -use crate::NixideError; - -pub trait AsErr { - fn as_err(self) -> Result<(), T>; -} - -impl AsErr for Option { - fn as_err(self) -> Result<(), NixideError> { - match self { - Some(err) => Err(err), - None => Ok(()), - } - } -} +// use crate::NixideError; +// +// pub trait AsErr { +// fn as_err(self) -> Result<(), T>; +// } +// +// impl AsErr for Option { +// fn as_err(self) -> Result<(), NixideError> { +// match self { +// Some(err) => Err(err), +// None => Ok(()), +// } +// } +// } +// +// pub trait FromC { +// /// Creates a new instance of [Self] from the underlying +// /// libnix C type [T]. +// unsafe fn from_c(value: T) -> Self; +// } pub trait AsInnerPtr { /// Get a pointer to the underlying (`inner`) `libnix` C struct. @@ -23,9 +29,3 @@ pub trait AsInnerPtr { /// in `unsafe` blocks for clarity. unsafe fn as_ptr(&self) -> *mut T; } - -pub trait FromC { - /// Creates a new instance of [Self] from the underlying - /// libnix C type [T]. - unsafe fn from_c(value: T) -> Self; -}