i'll get this macro one day
This commit is contained in:
parent
a186b2297b
commit
f3802f6300
4 changed files with 157 additions and 104 deletions
2
TODO.md
2
TODO.md
|
|
@ -1,3 +1,5 @@
|
||||||
|
- [ ] rename `AsInnerPtr::as_ptr` to `AsInnerPtr::as_mut_ptr`
|
||||||
|
|
||||||
- [ ] add NixError::from_nonnull that replaces calls to NonNull::new(...).ok_or(...)
|
- [ ] add NixError::from_nonnull that replaces calls to NonNull::new(...).ok_or(...)
|
||||||
- [ ] replace all `use nixide_sys as sys;` -> `use crate::sys;`
|
- [ ] replace all `use nixide_sys as sys;` -> `use crate::sys;`
|
||||||
- [ ] store NonNull pointers in structs!
|
- [ ] store NonNull pointers in structs!
|
||||||
|
|
|
||||||
|
|
@ -271,8 +271,8 @@ impl ErrorContext {
|
||||||
pub(super) fn get_nix_err_name(&self) -> Option<String> {
|
pub(super) fn get_nix_err_name(&self) -> Option<String> {
|
||||||
unsafe {
|
unsafe {
|
||||||
// NOTE: an Err here only occurs when "Last error was not a nix error"
|
// NOTE: an Err here only occurs when "Last error was not a nix error"
|
||||||
wrap::nix_string_callback(|callback, user_data, ctx| {
|
wrap::nix_string_callback!(|callback, userdata: *mut __UserData, ctx: &ErrorContext| {
|
||||||
sys::nix_err_name(ctx.as_ptr(), self.as_ptr(), Some(callback), user_data)
|
sys::nix_err_name(ctx.as_ptr(), self.as_ptr(), Some(callback), userdata)
|
||||||
})
|
})
|
||||||
.ok()
|
.ok()
|
||||||
}
|
}
|
||||||
|
|
@ -316,7 +316,7 @@ impl ErrorContext {
|
||||||
pub(super) fn get_nix_err_info_msg(&self) -> Option<String> {
|
pub(super) fn get_nix_err_info_msg(&self) -> Option<String> {
|
||||||
unsafe {
|
unsafe {
|
||||||
// NOTE: an Err here only occurs when "Last error was not a nix error"
|
// NOTE: an Err here only occurs when "Last error was not a nix error"
|
||||||
wrap::nix_string_callback(|callback, user_data, ctx| {
|
wrap::nix_string_callback!(|callback, user_data, ctx| {
|
||||||
sys::nix_err_info_msg(ctx.as_ptr(), self.as_ptr(), Some(callback), user_data)
|
sys::nix_err_info_msg(ctx.as_ptr(), self.as_ptr(), Some(callback), user_data)
|
||||||
})
|
})
|
||||||
.ok()
|
.ok()
|
||||||
|
|
|
||||||
|
|
@ -16,15 +16,14 @@ mod tests;
|
||||||
mod path;
|
mod path;
|
||||||
pub use path::*;
|
pub use path::*;
|
||||||
|
|
||||||
use std::ffi::CString;
|
use std::ffi::{c_char, c_void, CString};
|
||||||
use std::os::raw::c_char;
|
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use std::ptr::{null, null_mut, NonNull};
|
use std::ptr::{null, null_mut, NonNull};
|
||||||
use std::result::Result;
|
use std::result::Result;
|
||||||
|
|
||||||
use crate::errors::{new_nixide_error, ErrorContext};
|
use crate::errors::{new_nixide_error, ErrorContext};
|
||||||
use crate::stdext::CCharPtrExt;
|
use crate::stdext::CCharPtrExt;
|
||||||
use crate::util::wrap;
|
use crate::util::wrap::{self, UserData};
|
||||||
use crate::util::wrappers::AsInnerPtr;
|
use crate::util::wrappers::AsInnerPtr;
|
||||||
use crate::{NixideError, NixideResult};
|
use crate::{NixideError, NixideResult};
|
||||||
use nixide_sys as sys;
|
use nixide_sys as sys;
|
||||||
|
|
@ -149,36 +148,33 @@ impl Store {
|
||||||
// };
|
// };
|
||||||
|
|
||||||
wrap::nix_callback!(
|
wrap::nix_callback!(
|
||||||
|userdata ;
|
|userdata: fn(&str, &StorePath);
|
||||||
output_name_ptr: *const c_char,
|
output_name_ptr: *const c_char,
|
||||||
output_path_ptr: *const sys::StorePath|
|
output_path_ptr: *const sys::StorePath|
|
||||||
-> NixideResult<(String, StorePath)> {
|
-> NixideResult<(String, StorePath)> {
|
||||||
// XXX: TODO: test to see if this is ever null ("out" as a default feels unsafe...)
|
// 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());
|
// 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()?;
|
||||||
|
|
||||||
// SAFETY: out is a valid StorePath pointer from Nix, we need to clone it
|
let inner = wrap::nix_ptr_fn!(|ctx| unsafe {
|
||||||
// because Nix owns the original and may free it after the callback
|
sys::nix_store_path_clone(output_path_ptr as *mut sys::StorePath)
|
||||||
if !output_path_ptr.is_null() {
|
})?;
|
||||||
let inner = wrap::nix_ptr_fn!(|ctx| unsafe {
|
let store_path = StorePath { inner };
|
||||||
sys::nix_store_path_clone(output_path_ptr as *mut sys::StorePath)
|
|
||||||
})?;
|
|
||||||
let store_path = StorePath { inner };
|
|
||||||
|
|
||||||
callback(output_name.as_ref(), &store_path);
|
let callback = userdata.inner;
|
||||||
|
callback(output_name.as_ref(), &store_path);
|
||||||
|
|
||||||
Ok((output_name, store_path))
|
Ok((output_name, store_path))
|
||||||
} else {
|
|
||||||
panic!("IDK YET");
|
|
||||||
// XXX: TODO: how should `output_path_ptr.is_null()` be handled?
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
|callback, userdata, ctx: &ErrorContext| unsafe {
|
|callback,
|
||||||
|
state: *mut UserData<fn(&str, &StorePath), NixideResult<(String, StorePath)>>,
|
||||||
|
ctx: &ErrorContext| unsafe {
|
||||||
sys::nix_store_realise(
|
sys::nix_store_realise(
|
||||||
ctx.as_ptr(),
|
ctx.as_ptr(),
|
||||||
self.inner.as_ptr(),
|
self.inner.as_ptr(),
|
||||||
path.as_ptr(),
|
path.as_ptr(),
|
||||||
userdata,
|
(*state).inner_ptr() as *mut c_void,
|
||||||
Some(callback),
|
Some(callback),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,29 +1,47 @@
|
||||||
use std::mem::MaybeUninit;
|
#[repr(C)]
|
||||||
use std::os::raw::{c_char, c_uint, c_void};
|
#[derive(Debug)]
|
||||||
use std::path::PathBuf;
|
pub(crate) struct UserData<S, T> {
|
||||||
|
pub inner: S,
|
||||||
use crate::errors::{ErrorContext, NixideError};
|
pub retval: T,
|
||||||
use crate::stdext::CCharPtrExt as _;
|
|
||||||
use crate::util::wrappers::AsInnerPtr;
|
|
||||||
use crate::NixideResult;
|
|
||||||
|
|
||||||
struct UserData<T> {
|
|
||||||
// inner: T,
|
|
||||||
inner: MaybeUninit<T>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> AsInnerPtr<T> for UserData<T> {
|
impl<S, T> AsMut<UserData<S, T>> for UserData<S, T> {
|
||||||
unsafe fn as_ptr(&self) -> *mut T {
|
fn as_mut(&mut self) -> &mut UserData<S, T> {
|
||||||
self.inner.as_mut_ptr()
|
self
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<S, T> UserData<S, T> {
|
||||||
|
/// # 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
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe fn inner_ptr(&mut self) -> *mut S {
|
||||||
|
unsafe {
|
||||||
|
let ptr = self.as_mut_ptr();
|
||||||
|
&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 {
|
macro_rules! nonnull {
|
||||||
($ptr:expr $(,)? ) => {{
|
($ptr:expr $(,)? ) => {{
|
||||||
match ::std::ptr::NonNull::new($ptr) {
|
match ::std::ptr::NonNull::new($ptr) {
|
||||||
::std::option::Option::Some(p) => ::std::result::Result::Ok(p),
|
::std::option::Option::Some(p) => ::std::result::Result::Ok(p),
|
||||||
::std::option::Option::None => {
|
::std::option::Option::None => {
|
||||||
::std::result::Result::Err(crate::errors::new_nixide_error!(NullPtr))
|
::std::result::Result::Err($crate::errors::new_nixide_error!(NullPtr))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}};
|
}};
|
||||||
|
|
@ -32,116 +50,153 @@ pub(crate) use nonnull;
|
||||||
|
|
||||||
macro_rules! nix_fn {
|
macro_rules! nix_fn {
|
||||||
($callback:expr $(,)? ) => {{
|
($callback:expr $(,)? ) => {{
|
||||||
// XXX: TODO: what happens if i DO actually use `null_mut` instead of ErrorContext::new? does rust just panic?
|
let mut __ctx = $crate::errors::ErrorContext::new();
|
||||||
let mut ctx = crate::errors::ErrorContext::new();
|
let __result = $callback(&__ctx);
|
||||||
let result = $callback(
|
__ctx
|
||||||
&ctx,
|
.pop()
|
||||||
);
|
.and_then(|_| ::std::result::Result::Ok(__result))
|
||||||
ctx.pop().and_then(|_| ::std::result::Result::Ok(result))
|
|
||||||
}};
|
}};
|
||||||
}
|
}
|
||||||
pub(crate) use nix_fn;
|
pub(crate) use nix_fn;
|
||||||
|
|
||||||
macro_rules! nix_ptr_fn {
|
macro_rules! nix_ptr_fn {
|
||||||
($callback:expr $(,)? ) => {{
|
($callback:expr $(,)? ) => {{
|
||||||
crate::util::wrap::nix_fn!($callback).and_then(|ptr| crate::util::wrap::nonnull!(ptr))
|
$crate::util::wrap::nix_fn!($callback).and_then(|ptr| $crate::util::wrap::nonnull!(ptr))
|
||||||
}};
|
}};
|
||||||
}
|
}
|
||||||
pub(crate) use 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
|
||||||
|
}};
|
||||||
|
}
|
||||||
|
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
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
macro_rules! nix_callback {
|
macro_rules! nix_callback {
|
||||||
( | $($arg_name:ident : $arg_type:ty),* ; userdata $( : *mut c_void )? $(,)? | -> $ret:ty $body:block, $callback:expr $(,)? ) => {{
|
( | $userdata:ident : $userdata_type:ty; $($arg_name:ident : $arg_type: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)
|
// create a function item that wraps the closure body (so it has a concrete type)
|
||||||
#[allow(unused_variables)]
|
fn __captured_fn($userdata: &mut __UserData, $($arg_name: $arg_type),*) -> $ret $body
|
||||||
fn __captured_fn( $( $arg_name: $arg_type ),*, userdata: *mut ::std::os::raw::c_void) -> $ret $body
|
|
||||||
|
|
||||||
unsafe extern "C" fn __wrapper_callback(
|
unsafe extern "C" fn __wrapper_callback(
|
||||||
|
$userdata: *mut ::std::ffi::c_void,
|
||||||
$(
|
$(
|
||||||
$arg_name: $arg_type,
|
$arg_name: $arg_type,
|
||||||
)*
|
)*
|
||||||
userdata: *mut ::std::os::raw::c_void,
|
|
||||||
) {
|
) {
|
||||||
let result = unsafe { &mut *(userdata as *mut $ret) };
|
let ud = unsafe { &mut *($userdata as *mut __UserData) };
|
||||||
|
let stored_retval = &raw mut ud.retval;
|
||||||
|
|
||||||
*result = __captured_fn(
|
let retval = __captured_fn(
|
||||||
|
ud,
|
||||||
$(
|
$(
|
||||||
$arg_name,
|
$arg_name,
|
||||||
)*
|
)*
|
||||||
userdata,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
stored_retval.write(retval)
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// XXX: TODO: what happens if i DO actually use `null_mut` instead of ErrorContext::new? does rust just panic?
|
let mut __ctx: $crate::errors::ErrorContext = $crate::errors::ErrorContext::new();
|
||||||
let mut ctx = crate::errors::ErrorContext::new();
|
let mut __state: ::std::mem::MaybeUninit<__UserData> = ::std::mem::MaybeUninit::uninit();
|
||||||
let mut result: ::std::mem::MaybeUninit<$ret> = ::std::mem::MaybeUninit::uninit();
|
|
||||||
|
|
||||||
$callback(
|
// fn __captured_function(
|
||||||
__wrapper_callback,
|
// callback: unsafe extern "C" fn(
|
||||||
result.as_mut_ptr() as *mut ::std::os::raw::c_void,
|
// $userdata: *mut ::std::ffi::c_void,
|
||||||
&ctx,
|
// $(
|
||||||
);
|
// $arg_name: $arg_type,
|
||||||
ctx.pop().and_then(|_| unsafe { result.assume_init() })
|
// )*
|
||||||
|
// ),
|
||||||
|
// state: *mut __UserData,
|
||||||
|
// ctx: &$crate::errors::ErrorContext,
|
||||||
|
// ) {
|
||||||
|
// $function(callback, state, ctx);
|
||||||
|
// }
|
||||||
|
|
||||||
|
$function(__wrapper_callback, __state.as_mut_ptr(), &__ctx);
|
||||||
|
|
||||||
|
// add type annotations for compiler
|
||||||
|
__ctx.pop().and_then(|_| unsafe { __state.assume_init().retval })
|
||||||
}};
|
}};
|
||||||
|
|
||||||
( | userdata $( : *mut c_void )? ; $($arg_name:ident : $arg_type:ty),* | -> $ret:ty $body:block, $callback:expr $(,)? ) => {{
|
( | $($arg_name:ident : $arg_type:ty),* ; $userdata:ident : $userdata_type:ty $(,)? | -> $ret:ty $body:block, $callback: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)
|
// create a function item that wraps the closure body (so it has a concrete type)
|
||||||
#[allow(unused_variables)]
|
unsafe extern "C" fn __captured_fn( $( $arg_name: $arg_type ),*, $userdata: &mut __UserData) -> $ret $body
|
||||||
fn __captured_fn(userdata: *mut ::std::os::raw::c_void, $($arg_name: $arg_type),*) -> $ret $body
|
|
||||||
|
|
||||||
unsafe extern "C" fn __wrapper_callback(
|
unsafe extern "C" fn __wrapper_callback(
|
||||||
userdata: *mut ::std::os::raw::c_void,
|
|
||||||
$(
|
$(
|
||||||
$arg_name: $arg_type,
|
$arg_name: $arg_type,
|
||||||
)*
|
)*
|
||||||
|
$userdata: *mut ::std::ffi::c_void,
|
||||||
) {
|
) {
|
||||||
let result = unsafe { &mut *(userdata as *mut $ret) };
|
unsafe {
|
||||||
|
let ud = &mut *($userdata as *mut __UserData);
|
||||||
|
let stored_retval = &raw mut ud.retval;
|
||||||
|
|
||||||
*result = __captured_fn(
|
let retval = __captured_fn(
|
||||||
userdata,
|
$(
|
||||||
$(
|
$arg_name,
|
||||||
$arg_name,
|
)*
|
||||||
)*
|
ud,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
stored_retval.write(retval)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// XXX: TODO: what happens if i DO actually use `null_mut` instead of ErrorContext::new? does rust just panic?
|
// $crate::util::wrap::__nix_callback!($userdata_type, $ret, $callback)
|
||||||
let mut ctx = crate::errors::ErrorContext::new();
|
let mut __ctx = $crate::errors::ErrorContext::new();
|
||||||
let mut result: ::std::mem::MaybeUninit<$ret> = ::std::mem::MaybeUninit::uninit();
|
let mut __state: ::std::mem::MaybeUninit<
|
||||||
|
__UserData
|
||||||
|
> = ::std::mem::MaybeUninit::uninit();
|
||||||
|
|
||||||
$callback(
|
$callback(__wrapper_callback, __state.as_mut_ptr(), &__ctx);
|
||||||
__wrapper_callback,
|
|
||||||
result.as_mut_ptr() as *mut ::std::os::raw::c_void,
|
// add type annotations for compiler
|
||||||
&ctx,
|
let __return: $ret = __ctx.pop().and_then(|_| unsafe { __state.assume_init().retval });
|
||||||
);
|
__return
|
||||||
ctx.pop().and_then(|_| unsafe { result.assume_init() })
|
|
||||||
}};
|
}};
|
||||||
}
|
}
|
||||||
pub(crate) use nix_callback;
|
pub(crate) use nix_callback;
|
||||||
|
|
||||||
pub fn nix_string_callback<F>(callback: F) -> Result<String, NixideError>
|
// XXX: TODO: convert these to declarative macros
|
||||||
where
|
macro_rules! nix_string_callback {
|
||||||
F: FnOnce(
|
($callback:expr $(,)?) => {{
|
||||||
unsafe extern "C" fn(*const c_char, c_uint, *mut c_void),
|
$crate::util::wrap::nix_callback!(
|
||||||
*mut c_void,
|
|start: *const ::std::ffi::c_char, n: ::std::ffi::c_uint ; userdata: ()| -> $crate::NixideResult<String> {
|
||||||
&ErrorContext,
|
start.to_utf8_string_n(n as usize)
|
||||||
) -> i32,
|
},
|
||||||
{
|
$callback
|
||||||
crate::util::wrap::nix_callback!(
|
)
|
||||||
|start: *const c_char, n: c_uint ; userdata| -> NixideResult<String> {
|
}};
|
||||||
start.to_utf8_string_n(n as usize)
|
|
||||||
},
|
|
||||||
callback
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
pub(crate) use nix_string_callback;
|
||||||
|
|
||||||
pub fn nix_pathbuf_callback<F>(callback: F) -> Result<PathBuf, NixideError>
|
macro_rules! nix_pathbuf_callback {
|
||||||
where
|
($callback:expr $(,)?) => {{
|
||||||
F: FnOnce(
|
$crate::util::wrap::nix_string_callback!($callback).map(::std::path::PathBuf::from)
|
||||||
unsafe extern "C" fn(*const c_char, c_uint, *mut c_void),
|
}};
|
||||||
*mut c_void,
|
|
||||||
&ErrorContext,
|
|
||||||
) -> i32,
|
|
||||||
{
|
|
||||||
nix_string_callback(callback).map(::std::path::PathBuf::from)
|
|
||||||
}
|
}
|
||||||
|
pub(crate) use nix_pathbuf_callback;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue