major changes to internal wrapper utilities
This commit is contained in:
parent
4f77434320
commit
a186b2297b
15 changed files with 468 additions and 385 deletions
|
|
@ -24,11 +24,12 @@
|
||||||
use std::ffi::c_uint;
|
use std::ffi::c_uint;
|
||||||
use std::ptr::NonNull;
|
use std::ptr::NonNull;
|
||||||
|
|
||||||
use super::{NixError, NixideError, NixideResult};
|
use super::{NixError, NixideResult};
|
||||||
|
use crate::stdext::CCharPtrExt as _;
|
||||||
use crate::sys;
|
use crate::sys;
|
||||||
use crate::util::bindings::wrap_libnix_string_callback;
|
use crate::util::panic_issue_call_failed;
|
||||||
|
use crate::util::wrap;
|
||||||
use crate::util::wrappers::AsInnerPtr;
|
use crate::util::wrappers::AsInnerPtr;
|
||||||
use crate::util::{panic_issue_call_failed, CCharPtrNixExt};
|
|
||||||
|
|
||||||
/// This object stores error state.
|
/// This object stores error state.
|
||||||
///
|
///
|
||||||
|
|
@ -72,9 +73,22 @@ impl AsInnerPtr<sys::nix_c_context> for ErrorContext {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Into<NixideResult<()>> for &ErrorContext {
|
impl Into<NixideResult<()>> for &ErrorContext {
|
||||||
|
/// # Panics
|
||||||
|
///
|
||||||
|
/// This function will panic in the event that `context.get_err() == Some(err) && err == sys::nix_err_NIX_OK`
|
||||||
|
/// since `nixide::ErrorContext::get_err` is expected to return `None` to indicate `sys::nix_err_NIX_OK`.
|
||||||
|
///
|
||||||
|
/// This function will panic in the event that `value != sys::nix_err_NIX_OK`
|
||||||
|
/// but that `context.get_code() == sys::nix_err_NIX_OK`
|
||||||
fn into(self) -> NixideResult<()> {
|
fn into(self) -> NixideResult<()> {
|
||||||
let inner = self.get_err().ok_?;
|
let inner = match self.get_err() {
|
||||||
let msg = self.get_msg()?;
|
Some(err) => err,
|
||||||
|
None => return Ok(()),
|
||||||
|
};
|
||||||
|
let msg = match self.get_msg() {
|
||||||
|
Some(msg) => msg,
|
||||||
|
None => return Ok(()),
|
||||||
|
};
|
||||||
|
|
||||||
let err = match inner {
|
let err = match inner {
|
||||||
sys::nix_err_NIX_OK => unreachable!(),
|
sys::nix_err_NIX_OK => unreachable!(),
|
||||||
|
|
@ -95,7 +109,7 @@ impl Into<NixideResult<()>> for &ErrorContext {
|
||||||
err => NixError::Undocumented(err),
|
err => NixError::Undocumented(err),
|
||||||
};
|
};
|
||||||
|
|
||||||
Some(new_nixide_error!(NixError, inner, err, msg))
|
Err(new_nixide_error!(NixError, inner, err, msg))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -132,18 +146,13 @@ impl ErrorContext {
|
||||||
|
|
||||||
/// Check the error code and return an error if it's not `NIX_OK`.
|
/// Check the error code and return an error if it's not `NIX_OK`.
|
||||||
pub fn peak(&self) -> NixideResult<()> {
|
pub fn peak(&self) -> NixideResult<()> {
|
||||||
match self.into() {
|
self.into()
|
||||||
Some(err) => Err(err),
|
|
||||||
None => Ok(()),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
///
|
///
|
||||||
/// Equivalent to running `self.peak()` then `self.clear()`
|
/// Equivalent to running `self.peak()` then `self.clear()`
|
||||||
pub fn pop(&mut self) -> NixideResult<()> {
|
pub fn pop(&mut self) -> NixideResult<()> {
|
||||||
let error = self.peak();
|
self.peak().and_then(|_| Ok(self.clear()))
|
||||||
self.clear();
|
|
||||||
error
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// # Nix C++ API Internals
|
/// # Nix C++ API Internals
|
||||||
|
|
@ -214,13 +223,12 @@ impl ErrorContext {
|
||||||
/// Hence we can just test whether the returned pointer is a `NULL` pointer,
|
/// Hence we can just test whether the returned pointer is a `NULL` pointer,
|
||||||
/// and avoid passing in a [sys::nix_c_context] struct.
|
/// and avoid passing in a [sys::nix_c_context] struct.
|
||||||
pub(super) fn get_msg(&self) -> Option<String> {
|
pub(super) fn get_msg(&self) -> Option<String> {
|
||||||
// XXX: TODO: what happens if i DO actually use `null_mut` instead of ErrorContext::new? does rust just panic?
|
|
||||||
let ctx = ErrorContext::new();
|
let ctx = ErrorContext::new();
|
||||||
unsafe {
|
unsafe {
|
||||||
// NOTE: an Err here only occurs when `self.get_code() == Ok(())`
|
// NOTE: an Err here only occurs when `self.get_code() == Ok(())`
|
||||||
let mut n: c_uint = 0;
|
let mut n: c_uint = 0;
|
||||||
sys::nix_err_msg(ctx.as_ptr(), self.as_ptr(), &mut n)
|
sys::nix_err_msg(ctx.as_ptr(), self.as_ptr(), &mut n)
|
||||||
.to_utf8_string()
|
.to_utf8_string_n(n as usize)
|
||||||
.ok()
|
.ok()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -261,10 +269,9 @@ impl ErrorContext {
|
||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
pub(super) fn get_nix_err_name(&self) -> Option<String> {
|
pub(super) fn get_nix_err_name(&self) -> Option<String> {
|
||||||
// XXX: TODO: what happens if i DO actually use `null_mut` instead of ErrorContext::new? does rust just panic?
|
|
||||||
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_libnix_string_callback(|ctx, callback, user_data| {
|
wrap::nix_string_callback(|callback, user_data, ctx| {
|
||||||
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), user_data)
|
||||||
})
|
})
|
||||||
.ok()
|
.ok()
|
||||||
|
|
@ -307,10 +314,9 @@ impl ErrorContext {
|
||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
pub(super) fn get_nix_err_info_msg(&self) -> Option<String> {
|
pub(super) fn get_nix_err_info_msg(&self) -> Option<String> {
|
||||||
// XXX: TODO: what happens if i DO actually use `null_mut` instead of ErrorContext::new? does rust just panic?
|
|
||||||
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_libnix_string_callback(|ctx, callback, user_data| {
|
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()
|
||||||
|
|
@ -320,7 +326,6 @@ impl ErrorContext {
|
||||||
|
|
||||||
impl Drop for ErrorContext {
|
impl Drop for ErrorContext {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
// SAFETY: We own the context and it's valid until drop
|
|
||||||
unsafe {
|
unsafe {
|
||||||
sys::nix_c_context_free(self.inner.as_ptr());
|
sys::nix_c_context_free(self.inner.as_ptr());
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,7 @@
|
||||||
use std::fmt::{Display, Formatter, Result as FmtResult};
|
use std::fmt::{Display, Formatter, Result as FmtResult};
|
||||||
|
|
||||||
use super::{ErrorContext, NixError};
|
use super::NixError;
|
||||||
use crate::sys;
|
use crate::sys;
|
||||||
use crate::util::panic_issue_call_failed;
|
|
||||||
|
|
||||||
pub type NixideResult<T> = Result<T, NixideError>;
|
pub type NixideResult<T> = Result<T, NixideError>;
|
||||||
|
|
||||||
|
|
@ -44,38 +43,38 @@ pub enum NixideError {
|
||||||
|
|
||||||
macro_rules! new_nixide_error {
|
macro_rules! new_nixide_error {
|
||||||
(NixError, $inner:expr, $err:expr, $msg:expr) => {{
|
(NixError, $inner:expr, $err:expr, $msg:expr) => {{
|
||||||
NixideError::NixError {
|
crate::NixideError::NixError {
|
||||||
trace: stdext::debug_name!(),
|
trace: ::stdext::debug_name!(),
|
||||||
inner: $inner,
|
inner: $inner,
|
||||||
err: $err,
|
err: $err,
|
||||||
msg: $msg,
|
msg: $msg,
|
||||||
}
|
}
|
||||||
}};
|
}};
|
||||||
(StringNulByte) => {{
|
(StringNulByte) => {{
|
||||||
NixideError::StringNulByte {
|
crate::NixideError::StringNulByte {
|
||||||
trace: stdext::debug_name!(),
|
trace: ::stdext::debug_name!(),
|
||||||
}
|
}
|
||||||
}};
|
}};
|
||||||
(StringNotUtf8) => {{
|
(StringNotUtf8) => {{
|
||||||
NixideError::StringNotUtf8 {
|
crate::NixideError::StringNotUtf8 {
|
||||||
trace: stdext::debug_name!(),
|
trace: ::stdext::debug_name!(),
|
||||||
}
|
}
|
||||||
}};
|
}};
|
||||||
(NullPtr) => {{
|
(NullPtr) => {{
|
||||||
NixideError::NullPtr {
|
crate::NixideError::NullPtr {
|
||||||
trace: stdext::debug_name!(),
|
trace: ::stdext::debug_name!(),
|
||||||
}
|
}
|
||||||
}};
|
}};
|
||||||
(InvalidArg, $name:expr, $reason:expr) => {{
|
(InvalidArg, $name:expr, $reason:expr) => {{
|
||||||
NixideError::InvalidArg {
|
crate::NixideError::InvalidArg {
|
||||||
trace: stdext::debug_name!(),
|
trace: ::stdext::debug_name!(),
|
||||||
name: $name,
|
name: $name,
|
||||||
reason: $reason,
|
reason: $reason,
|
||||||
}
|
}
|
||||||
}};
|
}};
|
||||||
(InvalidType, $expected:expr, $got:expr) => {{
|
(InvalidType, $expected:expr, $got:expr) => {{
|
||||||
NixideError::InvalidType {
|
crate::NixideError::InvalidType {
|
||||||
trace: stdext::debug_name!(),
|
trace: ::stdext::debug_name!(),
|
||||||
expected: $expected,
|
expected: $expected,
|
||||||
got: $got,
|
got: $got,
|
||||||
}
|
}
|
||||||
|
|
@ -83,49 +82,14 @@ macro_rules! new_nixide_error {
|
||||||
}
|
}
|
||||||
pub(crate) use new_nixide_error;
|
pub(crate) use new_nixide_error;
|
||||||
|
|
||||||
|
#[allow(unused_macros)]
|
||||||
macro_rules! retrace_nixide_error {
|
macro_rules! retrace_nixide_error {
|
||||||
($x:expr) => {{
|
($x:expr) => {{
|
||||||
new_nixide_error!($x.err)
|
crate::errors::new_nixide_error!($x.err)
|
||||||
}};
|
}};
|
||||||
}
|
}
|
||||||
pub(crate) use retrace_nixide_error;
|
pub(crate) use retrace_nixide_error;
|
||||||
|
|
||||||
impl NixideError {
|
|
||||||
/// # Panics
|
|
||||||
///
|
|
||||||
/// This function will panic in the event that `context.get_err() == Some(err) && err == sys::nix_err_NIX_OK`
|
|
||||||
/// since `nixide::ErrorContext::get_err` is expected to return `None` to indicate `sys::nix_err_NIX_OK`.
|
|
||||||
///
|
|
||||||
///
|
|
||||||
/// This function will panic in the event that `value != sys::nix_err_NIX_OK`
|
|
||||||
/// but that `context.get_code() == sys::nix_err_NIX_OK`
|
|
||||||
pub(super) fn from_error_context(context: &ErrorContext) -> Option<NixideError> {
|
|
||||||
let inner = context.get_err()?;
|
|
||||||
let msg = context.get_msg()?;
|
|
||||||
|
|
||||||
let err = match inner {
|
|
||||||
sys::nix_err_NIX_OK => unreachable!(),
|
|
||||||
|
|
||||||
sys::nix_err_NIX_ERR_OVERFLOW => NixError::Overflow,
|
|
||||||
sys::nix_err_NIX_ERR_KEY => NixError::KeyNotFound(None),
|
|
||||||
sys::nix_err_NIX_ERR_NIX_ERROR => NixError::ExprEval {
|
|
||||||
name: context
|
|
||||||
.get_nix_err_name()
|
|
||||||
.unwrap_or_else(|| panic_issue_call_failed!()),
|
|
||||||
|
|
||||||
info_msg: context
|
|
||||||
.get_nix_err_info_msg()
|
|
||||||
.unwrap_or_else(|| panic_issue_call_failed!()),
|
|
||||||
},
|
|
||||||
|
|
||||||
sys::nix_err_NIX_ERR_UNKNOWN => NixError::Unknown,
|
|
||||||
err => NixError::Undocumented(err),
|
|
||||||
};
|
|
||||||
|
|
||||||
Some(new_nixide_error!(NixError, inner, err, msg))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl std::error::Error for NixideError {}
|
impl std::error::Error for NixideError {}
|
||||||
|
|
||||||
impl Display for NixideError {
|
impl Display for NixideError {
|
||||||
|
|
@ -172,3 +136,16 @@ impl Display for NixideError {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub trait AsErr<T> {
|
||||||
|
fn as_err(self) -> Result<(), T>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AsErr<NixideError> for Option<NixideError> {
|
||||||
|
fn as_err(self) -> Result<(), NixideError> {
|
||||||
|
match self {
|
||||||
|
Some(err) => Err(err),
|
||||||
|
None => Ok(()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -98,36 +98,6 @@ pub enum NixError {
|
||||||
Undocumented(sys::nix_err),
|
Undocumented(sys::nix_err),
|
||||||
}
|
}
|
||||||
|
|
||||||
// impl NixError {
|
|
||||||
// /// # Panics
|
|
||||||
// ///
|
|
||||||
// /// This function will panic in the event that `context.get_err() == Some(err) && err == sys::nix_err_NIX_OK`
|
|
||||||
// /// since `nixide::ErrorContext::get_err` is expected to return `None` to indicate `sys::ni_err_NIX_OK`.
|
|
||||||
// ///
|
|
||||||
// ///
|
|
||||||
// /// This function will panic in the event that `value != sys::nix_err_NIX_OK`
|
|
||||||
// /// but that `context.get_code() == sys::nix_err_NIX_OK`
|
|
||||||
// pub(super) fn from_error_context(context: &ErrorContext) -> Option<NixError> {
|
|
||||||
// #[allow(nonstandard_style)]
|
|
||||||
// match context.get_err()? {
|
|
||||||
// sys::nix_err_NIX_OK => unreachable!("call to `nixide::ErrorContext::get_err@nixide::NixError::from_context` failed: please open an issue on https://github.com/cry128/nixide"),
|
|
||||||
|
|
||||||
// sys::nix_err_NIX_ERR_OVERFLOW => Some(NixError::Overflow),
|
|
||||||
// sys::nix_err_NIX_ERR_KEY => Some(NixError::KeyNotFound(None)),
|
|
||||||
// sys::nix_err_NIX_ERR_NIX_ERROR => Some(NixError::ExprEval {
|
|
||||||
// name: context
|
|
||||||
// .get_nix_err_name()
|
|
||||||
// .expect("call to `nixide::ErrorContext::get_nix_err_name@nixide::NixError::from_context` failed: please open an issue on https://github.com/cry128/nixide"),
|
|
||||||
// info_msg: context.get_nix_err_info_msg()
|
|
||||||
// .expect("call to `nixide::ErrorContext::get_nix_err_info_msg@nixide::NixError::from_context` failed: please open an issue on https://github.com/cry128/nixide"),
|
|
||||||
// }),
|
|
||||||
|
|
||||||
// sys::nix_err_NIX_ERR_UNKNOWN => Some(NixError::Unknown),
|
|
||||||
// err => Some(NixError::Undocumented(err)),
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
impl Display for NixError {
|
impl Display for NixError {
|
||||||
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
|
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
|
||||||
match self {
|
match self {
|
||||||
|
|
|
||||||
|
|
@ -40,7 +40,7 @@ impl Value {
|
||||||
let ctx = ErrorContext::new();
|
let ctx = ErrorContext::new();
|
||||||
|
|
||||||
unsafe { sys::nix_value_force(ctx.as_ptr(), state.as_ptr(), self.as_ptr()) };
|
unsafe { sys::nix_value_force(ctx.as_ptr(), state.as_ptr(), self.as_ptr()) };
|
||||||
ctx.peak().as_err()
|
ctx.peak()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Force deep evaluation of this value.
|
/// Force deep evaluation of this value.
|
||||||
|
|
@ -54,7 +54,7 @@ impl Value {
|
||||||
let ctx = ErrorContext::new();
|
let ctx = ErrorContext::new();
|
||||||
|
|
||||||
unsafe { sys::nix_value_force_deep(ctx.as_ptr(), state.as_ptr(), self.as_ptr()) };
|
unsafe { sys::nix_value_force_deep(ctx.as_ptr(), state.as_ptr(), self.as_ptr()) };
|
||||||
ctx.peak().as_err()
|
ctx.peak()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the type of this value.
|
/// Get the type of this value.
|
||||||
|
|
@ -66,9 +66,7 @@ impl Value {
|
||||||
// NOTE: an error here only occurs if `nix_get_type` catches an error,
|
// NOTE: an error here only occurs if `nix_get_type` catches an error,
|
||||||
// NOTE: which in turn only happens if the `sys::nix_value*` is a null pointer
|
// NOTE: which in turn only happens if the `sys::nix_value*` is a null pointer
|
||||||
// NOTE: or points to an uninitialised `nix_value` struct.
|
// NOTE: or points to an uninitialised `nix_value` struct.
|
||||||
ctx.peak()
|
ctx.peak().unwrap_or_else(|_| panic!("TODO im sleepy rn"));
|
||||||
.as_err()
|
|
||||||
.unwrap_or_else(|_| panic!("TODO im sleepy rn"));
|
|
||||||
value_type
|
value_type
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ use std::ptr::{null_mut, NonNull};
|
||||||
use super::{FetchersSettings, FlakeReferenceParseFlags, FlakeSettings};
|
use super::{FetchersSettings, FlakeReferenceParseFlags, FlakeSettings};
|
||||||
use crate::errors::new_nixide_error;
|
use crate::errors::new_nixide_error;
|
||||||
use crate::sys;
|
use crate::sys;
|
||||||
use crate::util::bindings::wrap_libnix_string_callback;
|
use crate::util::bindings::wrap_nix_string_callback;
|
||||||
use crate::util::wrappers::AsInnerPtr;
|
use crate::util::wrappers::AsInnerPtr;
|
||||||
use crate::NixideError;
|
use crate::NixideError;
|
||||||
|
|
||||||
|
|
@ -36,7 +36,7 @@ impl FlakeReference {
|
||||||
reference: &str,
|
reference: &str,
|
||||||
) -> Result<(FlakeReference, String), NixideError> {
|
) -> Result<(FlakeReference, String), NixideError> {
|
||||||
let mut ptr: *mut sys::nix_flake_reference = null_mut();
|
let mut ptr: *mut sys::nix_flake_reference = null_mut();
|
||||||
let result = wrap_libnix_string_callback(|ctx, callback, user_data| unsafe {
|
let result = wrap_nix_string_callback(|ctx, callback, user_data| unsafe {
|
||||||
sys::nix_flake_reference_and_fragment_from_string(
|
sys::nix_flake_reference_and_fragment_from_string(
|
||||||
ctx.as_ptr(),
|
ctx.as_ptr(),
|
||||||
fetch_settings.as_ptr(),
|
fetch_settings.as_ptr(),
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
// #![warn(missing_docs)]
|
// #![warn(missing_docs)]
|
||||||
|
|
||||||
pub(crate) mod errors;
|
pub(crate) mod errors;
|
||||||
mod expr;
|
// mod expr;
|
||||||
mod flake;
|
// mod flake;
|
||||||
mod stdext;
|
mod stdext;
|
||||||
mod store;
|
mod store;
|
||||||
pub(crate) mod util;
|
pub(crate) mod util;
|
||||||
|
|
@ -10,7 +10,7 @@ mod verbosity;
|
||||||
mod version;
|
mod version;
|
||||||
|
|
||||||
pub use errors::{NixError, NixideError, NixideResult};
|
pub use errors::{NixError, NixideError, NixideResult};
|
||||||
pub use expr::{EvalState, EvalStateBuilder, Value, ValueType};
|
// pub use expr::{EvalState, EvalStateBuilder, Value, ValueType};
|
||||||
pub use store::{Store, StorePath};
|
pub use store::{Store, StorePath};
|
||||||
pub use verbosity::NixVerbosity;
|
pub use verbosity::NixVerbosity;
|
||||||
pub use version::NixVersion;
|
pub use version::NixVersion;
|
||||||
|
|
|
||||||
|
|
@ -1,30 +1,64 @@
|
||||||
use std::ffi::{c_char, CStr};
|
use std::ffi::{c_char, CStr};
|
||||||
use std::slice::from_raw_parts;
|
use std::slice::from_raw_parts;
|
||||||
use std::str::{from_utf8, Utf8Error};
|
use std::str::from_utf8;
|
||||||
|
|
||||||
|
use crate::errors::new_nixide_error;
|
||||||
|
use crate::NixideResult;
|
||||||
|
|
||||||
pub trait CCharPtrExt {
|
pub trait CCharPtrExt {
|
||||||
fn to_utf8_string(self) -> Result<String, Option<Utf8Error>>;
|
fn to_utf8_string(self) -> NixideResult<String>;
|
||||||
|
|
||||||
fn to_utf8_string_n(self, n: usize) -> Result<String, Option<Utf8Error>>;
|
fn to_utf8_string_n(self, n: usize) -> NixideResult<String>;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CCharPtrExt for *const c_char {
|
impl CCharPtrExt for *const c_char {
|
||||||
fn to_utf8_string(self) -> Result<String, Option<Utf8Error>> {
|
fn to_utf8_string(self) -> NixideResult<String> {
|
||||||
if self.is_null() {
|
if self.is_null() {
|
||||||
return Err(None);
|
return Err(new_nixide_error!(NullPtr));
|
||||||
}
|
}
|
||||||
let cstr = unsafe { CStr::from_ptr(self) };
|
let cstr = unsafe { CStr::from_ptr(self) };
|
||||||
match cstr.to_str() {
|
match cstr.to_str() {
|
||||||
Ok(s) => Ok(s.to_owned()),
|
Ok(s) => Ok(s.to_owned()),
|
||||||
Err(err) => Err(Some(err)),
|
Err(_) => Err(new_nixide_error!(StringNotUtf8)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn to_utf8_string_n(self, n: usize) -> Result<String, Option<Utf8Error>> {
|
fn to_utf8_string_n(self, n: usize) -> NixideResult<String> {
|
||||||
if self.is_null() || n == 0 {
|
if self.is_null() || n == 0 {
|
||||||
return Err(None);
|
return Err(new_nixide_error!(NullPtr));
|
||||||
}
|
}
|
||||||
let bytes = unsafe { from_raw_parts(self.cast::<u8>(), n as usize) };
|
let bytes = unsafe { from_raw_parts(self.cast::<u8>(), n as usize) };
|
||||||
from_utf8(bytes).map(str::to_string).map_err(Some)
|
match from_utf8(bytes) {
|
||||||
|
Ok(s) => Ok(s.to_string()),
|
||||||
|
Err(_) => Err(new_nixide_error!(StringNotUtf8)),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// XXX: TODO: remove if unused
|
||||||
|
// pub trait CCharPtrExt {
|
||||||
|
// fn to_utf8_string(self) -> Result<String, Option<Utf8Error>>;
|
||||||
|
//
|
||||||
|
// fn to_utf8_string_n(self, n: usize) -> Result<String, Option<Utf8Error>>;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// impl CCharPtrExt for *const c_char {
|
||||||
|
// fn to_utf8_string(self) -> Result<String, Option<Utf8Error>> {
|
||||||
|
// if self.is_null() {
|
||||||
|
// return Err(None);
|
||||||
|
// }
|
||||||
|
// let cstr = unsafe { CStr::from_ptr(self) };
|
||||||
|
// match cstr.to_str() {
|
||||||
|
// Ok(s) => Ok(s.to_owned()),
|
||||||
|
// Err(err) => Err(Some(err)),
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// fn to_utf8_string_n(self, n: usize) -> Result<String, Option<Utf8Error>> {
|
||||||
|
// if self.is_null() || n == 0 {
|
||||||
|
// return Err(None);
|
||||||
|
// }
|
||||||
|
// let bytes = unsafe { from_raw_parts(self.cast::<u8>(), n as usize) };
|
||||||
|
// from_utf8(bytes).map(str::to_string).map_err(Some)
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
|
||||||
|
|
@ -16,15 +16,17 @@ mod tests;
|
||||||
mod path;
|
mod path;
|
||||||
pub use path::*;
|
pub use path::*;
|
||||||
|
|
||||||
use std::ffi::{CStr, CString, NulError};
|
use std::ffi::CString;
|
||||||
use std::os::raw::{c_char, c_void};
|
use std::os::raw::c_char;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use std::ptr::NonNull;
|
use std::ptr::{null, null_mut, NonNull};
|
||||||
use std::result::Result;
|
use std::result::Result;
|
||||||
use std::sync::Arc;
|
|
||||||
|
|
||||||
use super::{ErrorContext, NixErrorCode};
|
use crate::errors::{new_nixide_error, ErrorContext};
|
||||||
use crate::util::bindings::{wrap_libnix_pathbuf_callback, wrap_libnix_string_callback};
|
use crate::stdext::CCharPtrExt;
|
||||||
|
use crate::util::wrap;
|
||||||
|
use crate::util::wrappers::AsInnerPtr;
|
||||||
|
use crate::{NixideError, NixideResult};
|
||||||
use nixide_sys as sys;
|
use nixide_sys as sys;
|
||||||
|
|
||||||
/// Nix store for managing packages and derivations.
|
/// Nix store for managing packages and derivations.
|
||||||
|
|
@ -32,7 +34,12 @@ use nixide_sys as sys;
|
||||||
/// The store provides access to Nix packages, derivations, and store paths.
|
/// The store provides access to Nix packages, derivations, and store paths.
|
||||||
pub struct Store {
|
pub struct Store {
|
||||||
pub(crate) inner: NonNull<sys::Store>,
|
pub(crate) inner: NonNull<sys::Store>,
|
||||||
pub(crate) _context: Arc<ErrorContext>,
|
}
|
||||||
|
|
||||||
|
impl AsInnerPtr<sys::Store> for Store {
|
||||||
|
unsafe fn as_ptr(&self) -> *mut sys::Store {
|
||||||
|
self.inner.as_ptr()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Store {
|
impl Store {
|
||||||
|
|
@ -46,36 +53,19 @@ impl Store {
|
||||||
/// # Errors
|
/// # Errors
|
||||||
///
|
///
|
||||||
/// Returns an error if the store cannot be opened.
|
/// Returns an error if the store cannot be opened.
|
||||||
pub fn open(context: &Arc<ErrorContext>, uri: Option<&str>) -> Result<Self, NixErrorCode> {
|
pub fn open(uri: Option<&str>) -> Result<Self, NixideError> {
|
||||||
let uri_cstring: CString;
|
let uri_ptr = match uri.map(CString::new) {
|
||||||
let uri_ptr = if let Some(uri) = uri {
|
Some(Ok(c_uri)) => c_uri.as_ptr(),
|
||||||
uri_cstring = NixErrorCode::from_nulerror(CString::new(uri), "nixide::Store::open")?;
|
Some(Err(_)) => Err(new_nixide_error!(StringNulByte))?,
|
||||||
uri_cstring.as_ptr()
|
None => null(),
|
||||||
} else {
|
|
||||||
std::ptr::null()
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// SAFETY: context is valid, uri_ptr is either null or valid CString
|
let inner = wrap::nix_ptr_fn!(|ctx: &ErrorContext| unsafe {
|
||||||
let store_ptr =
|
// XXX: TODO: allow args to be parsed instead of just `null_mut`
|
||||||
unsafe { sys::nix_store_open(context.as_ptr(), uri_ptr, std::ptr::null_mut()) };
|
sys::nix_store_open(ctx.as_ptr(), uri_ptr, null_mut())
|
||||||
|
|
||||||
let inner = NonNull::new(store_ptr).ok_or(NixErrorCode::NullPtr {
|
|
||||||
location: "nix_store_open",
|
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
Ok(Store {
|
Ok(Store { inner })
|
||||||
inner,
|
|
||||||
_context: Arc::clone(context),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get the raw store pointer.
|
|
||||||
///
|
|
||||||
/// # Safety
|
|
||||||
///
|
|
||||||
/// The caller must ensure the pointer is used safely.
|
|
||||||
pub(crate) unsafe fn as_ptr(&self) -> *mut sys::Store {
|
|
||||||
self.inner.as_ptr()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Realize a store path.
|
/// Realize a store path.
|
||||||
|
|
@ -99,72 +89,100 @@ impl Store {
|
||||||
&self,
|
&self,
|
||||||
path: &StorePath,
|
path: &StorePath,
|
||||||
callback: fn(&str, &StorePath),
|
callback: fn(&str, &StorePath),
|
||||||
) -> Result<Vec<(String, StorePath)>, NixErrorCode> {
|
) -> NixideResult<(String, StorePath)> {
|
||||||
// Type alias for our userdata: (outputs vector, context)
|
// // Type alias for our userdata: (outputs vector, context)
|
||||||
type Userdata = (
|
// type Userdata = (
|
||||||
Vec<(String, StorePath)>,
|
// Vec<(String, StorePath)>,
|
||||||
Arc<ErrorContext>,
|
// Arc<ErrorContext>,
|
||||||
fn(&str, &StorePath),
|
// 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<Context>) 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),
|
||||||
|
// )
|
||||||
|
// };
|
||||||
|
|
||||||
// Callback function that will be called for each realized output
|
wrap::nix_callback!(
|
||||||
unsafe extern "C" fn realise_callback(
|
|userdata ;
|
||||||
userdata: *mut c_void,
|
output_name_ptr: *const c_char,
|
||||||
out_name_ptr: *const c_char,
|
output_path_ptr: *const sys::StorePath|
|
||||||
out_path_ptr: *const sys::StorePath,
|
-> NixideResult<(String, StorePath)> {
|
||||||
) {
|
// XXX: TODO: test to see if this is ever null ("out" as a default feels unsafe...)
|
||||||
// SAFETY: userdata is a valid pointer to our (Vec, Arc<Context>) tuple
|
// let output_name = output_name_ptr.to_utf8_string().unwrap_or("out".to_owned());
|
||||||
let (outputs, context, callback) = unsafe { &mut *(userdata as *mut Userdata) };
|
let output_name = output_name_ptr.to_utf8_string()?;
|
||||||
|
|
||||||
// SAFETY: outname is a valid C string from Nix
|
// SAFETY: out is a valid StorePath pointer from Nix, we need to clone it
|
||||||
let output_name = if !out_name_ptr.is_null() {
|
// because Nix owns the original and may free it after the callback
|
||||||
unsafe { CStr::from_ptr(out_name_ptr).to_string_lossy().into_owned() }
|
if !output_path_ptr.is_null() {
|
||||||
} else {
|
let inner = wrap::nix_ptr_fn!(|ctx| unsafe {
|
||||||
String::from("out") // Default output name
|
sys::nix_store_path_clone(output_path_ptr as *mut sys::StorePath)
|
||||||
};
|
})?;
|
||||||
|
let store_path = StorePath { inner };
|
||||||
// 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,
|
|
||||||
_context: Arc::clone(context),
|
|
||||||
};
|
|
||||||
|
|
||||||
callback(output_name.as_ref(), &store_path);
|
callback(output_name.as_ref(), &store_path);
|
||||||
|
|
||||||
outputs.push((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 {
|
||||||
|
sys::nix_store_realise(
|
||||||
|
ctx.as_ptr(),
|
||||||
|
self.inner.as_ptr(),
|
||||||
|
path.as_ptr(),
|
||||||
|
userdata,
|
||||||
|
Some(callback),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
)
|
||||||
|
|
||||||
// Create userdata with empty outputs vector and context
|
|
||||||
let mut userdata: Userdata = (Vec::new(), Arc::clone(&self._context), 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(
|
|
||||||
self._context.as_ptr(),
|
|
||||||
self.inner.as_ptr(),
|
|
||||||
path.as_ptr(),
|
|
||||||
userdata_ptr,
|
|
||||||
Some(realise_callback),
|
|
||||||
)
|
|
||||||
};
|
|
||||||
|
|
||||||
NixErrorCode::result_from(err, "nix_store_realise")?;
|
|
||||||
|
|
||||||
// Return the collected outputs
|
|
||||||
Ok(userdata.0)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parse a store path string into a StorePath.
|
/// Parse a store path string into a StorePath.
|
||||||
|
|
@ -191,40 +209,30 @@ impl Store {
|
||||||
/// # Ok(())
|
/// # Ok(())
|
||||||
/// # }
|
/// # }
|
||||||
/// ```
|
/// ```
|
||||||
pub fn store_path(&self, path: &str) -> Result<StorePath, NixErrorCode> {
|
pub fn store_path(&self, path: &str) -> Result<StorePath, NixideError> {
|
||||||
StorePath::parse(&self._context, self, path)
|
StorePath::parse(self, path)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the version of a Nix store
|
/// Get the version of a Nix store
|
||||||
///
|
///
|
||||||
/// If the store doesn't have a version (like the dummy store), returns None
|
/// If the store doesn't have a version (like the dummy store), returns None
|
||||||
pub fn version(&self) -> Result<String, NixErrorCode> {
|
pub fn version(&self) -> Result<String, NixideError> {
|
||||||
wrap_libnix_string_callback("nix_store_get_version", |callback, user_data| unsafe {
|
wrap::nix_string_callback(|callback, user_data, ctx| unsafe {
|
||||||
sys::nix_store_get_version(
|
sys::nix_store_get_version(ctx.as_ptr(), self.inner.as_ptr(), Some(callback), user_data)
|
||||||
self._context.as_ptr(),
|
|
||||||
self.inner.as_ptr(),
|
|
||||||
Some(callback),
|
|
||||||
user_data,
|
|
||||||
)
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the URI of a Nix store
|
/// Get the URI of a Nix store
|
||||||
pub fn uri(&self) -> Result<String, NixErrorCode> {
|
pub fn uri(&self) -> Result<String, NixideError> {
|
||||||
wrap_libnix_string_callback("nix_store_get_uri", |callback, user_data| unsafe {
|
wrap::nix_string_callback(|callback, user_data, ctx| unsafe {
|
||||||
sys::nix_store_get_uri(
|
sys::nix_store_get_uri(ctx.as_ptr(), self.inner.as_ptr(), Some(callback), user_data)
|
||||||
self._context.as_ptr(),
|
|
||||||
self.inner.as_ptr(),
|
|
||||||
Some(callback),
|
|
||||||
user_data,
|
|
||||||
)
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn store_dir(&self) -> Result<PathBuf, NixErrorCode> {
|
pub fn store_dir(&self) -> Result<PathBuf, NixideError> {
|
||||||
wrap_libnix_pathbuf_callback("nix_store_get_storedir", |callback, user_data| unsafe {
|
wrap::nix_pathbuf_callback(|callback, user_data, ctx| unsafe {
|
||||||
sys::nix_store_get_storedir(
|
sys::nix_store_get_storedir(
|
||||||
self._context.as_ptr(),
|
ctx.as_ptr(),
|
||||||
self.inner.as_ptr(),
|
self.inner.as_ptr(),
|
||||||
Some(callback),
|
Some(callback),
|
||||||
user_data,
|
user_data,
|
||||||
|
|
@ -236,38 +244,35 @@ impl Store {
|
||||||
&self,
|
&self,
|
||||||
dst_store: &Store,
|
dst_store: &Store,
|
||||||
store_path: &StorePath,
|
store_path: &StorePath,
|
||||||
) -> Result<(), NixErrorCode> {
|
) -> Result<(), NixideError> {
|
||||||
let err = unsafe {
|
wrap::nix_fn!(|ctx: &ErrorContext| unsafe {
|
||||||
sys::nix_store_copy_closure(
|
sys::nix_store_copy_closure(
|
||||||
self._context.as_ptr(),
|
ctx.as_ptr(),
|
||||||
self.inner.as_ptr(),
|
self.as_ptr(),
|
||||||
dst_store.inner.as_ptr(),
|
dst_store.as_ptr(),
|
||||||
store_path.inner.as_ptr(),
|
store_path.as_ptr(),
|
||||||
)
|
); // semi-colon to return () and not i32
|
||||||
};
|
})
|
||||||
NixErrorCode::result_from(err, "nix_store_copy_closure")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn copy_closure_from(
|
pub fn copy_closure_from(
|
||||||
&self,
|
&self,
|
||||||
src_store: &Store,
|
src_store: &Store,
|
||||||
store_path: &StorePath,
|
store_path: &StorePath,
|
||||||
) -> Result<(), NixErrorCode> {
|
) -> Result<(), NixideError> {
|
||||||
let err = unsafe {
|
wrap::nix_fn!(|ctx: &ErrorContext| unsafe {
|
||||||
sys::nix_store_copy_closure(
|
sys::nix_store_copy_closure(
|
||||||
self._context.as_ptr(),
|
ctx.as_ptr(),
|
||||||
src_store.inner.as_ptr(),
|
src_store.as_ptr(),
|
||||||
self.inner.as_ptr(),
|
self.as_ptr(),
|
||||||
store_path.inner.as_ptr(),
|
store_path.inner.as_ptr(),
|
||||||
)
|
);
|
||||||
};
|
}) // semi-colon to return () and not i32
|
||||||
NixErrorCode::result_from(err, "nix_store_copy_closure")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Drop for Store {
|
impl Drop for Store {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
// SAFETY: We own the store and it's valid until drop
|
|
||||||
unsafe {
|
unsafe {
|
||||||
sys::nix_store_free(self.inner.as_ptr());
|
sys::nix_store_free(self.inner.as_ptr());
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,11 @@
|
||||||
use std::ffi::CString;
|
use std::ffi::CString;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use std::ptr::NonNull;
|
use std::ptr::NonNull;
|
||||||
use std::sync::Arc;
|
|
||||||
|
|
||||||
use super::Store;
|
use super::Store;
|
||||||
use crate::errors::{new_nixide_error, ErrorContext};
|
use crate::errors::{new_nixide_error, ErrorContext};
|
||||||
use crate::util::bindings::{wrap_libnix_pathbuf_callback, wrap_libnix_string_callback};
|
use crate::util::panic_issue_call_failed;
|
||||||
|
use crate::util::wrap;
|
||||||
use crate::util::wrappers::AsInnerPtr;
|
use crate::util::wrappers::AsInnerPtr;
|
||||||
use crate::NixideError;
|
use crate::NixideError;
|
||||||
|
|
||||||
|
|
@ -14,10 +14,17 @@ use nixide_sys::{self as sys, nix_err_NIX_OK};
|
||||||
/// A path in the Nix store.
|
/// A path in the Nix store.
|
||||||
///
|
///
|
||||||
/// Represents a store path that can be realized, queried, or manipulated.
|
/// Represents a store path that can be realized, queried, or manipulated.
|
||||||
|
///
|
||||||
pub struct StorePath {
|
pub struct StorePath {
|
||||||
pub(crate) inner: NonNull<sys::StorePath>,
|
pub(crate) inner: NonNull<sys::StorePath>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl AsInnerPtr<sys::StorePath> for StorePath {
|
||||||
|
unsafe fn as_ptr(&self) -> *mut sys::StorePath {
|
||||||
|
self.inner.as_ptr()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl StorePath {
|
impl StorePath {
|
||||||
/// Parse a store path string into a StorePath.
|
/// Parse a store path string into a StorePath.
|
||||||
///
|
///
|
||||||
|
|
@ -30,24 +37,13 @@ impl StorePath {
|
||||||
///
|
///
|
||||||
/// Returns an error if the path cannot be parsed.
|
/// Returns an error if the path cannot be parsed.
|
||||||
pub fn parse(store: &Store, path: &str) -> Result<Self, NixideError> {
|
pub fn parse(store: &Store, path: &str) -> Result<Self, NixideError> {
|
||||||
let path_cstring = CString::new(path).or(Err(new_nixide_error!(
|
let c_path = CString::new(path).or(Err(new_nixide_error!(StringNulByte)))?;
|
||||||
InvalidArg,
|
|
||||||
"path",
|
|
||||||
"contains a `\\0` (NUL) byte".to_owned()
|
|
||||||
)))?;
|
|
||||||
|
|
||||||
let ctx = ErrorContext::new();
|
let inner = wrap::nix_ptr_fn!(|ctx: &ErrorContext| unsafe {
|
||||||
let path_ptr = unsafe {
|
sys::nix_store_parse_path(ctx.as_ptr(), store.as_ptr(), c_path.as_ptr())
|
||||||
sys::nix_store_parse_path(ctx.as_ptr(), store.as_ptr(), path_cstring.as_ptr())
|
})?;
|
||||||
};
|
|
||||||
|
|
||||||
match ctx.peak() {
|
Ok(Self { inner })
|
||||||
Some(err) => Err(err),
|
|
||||||
None => match NonNull::new(path_ptr) {
|
|
||||||
Some(inner) => Ok(Self { inner }),
|
|
||||||
None => Err(new_nixide_error!(NullPtr)),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the name component of the store path.
|
/// Get the name component of the store path.
|
||||||
|
|
@ -60,9 +56,8 @@ impl StorePath {
|
||||||
/// Returns an error if the name cannot be retrieved.
|
/// Returns an error if the name cannot be retrieved.
|
||||||
///
|
///
|
||||||
pub fn name(&self) -> Result<String, NixideError> {
|
pub fn name(&self) -> Result<String, NixideError> {
|
||||||
wrap_libnix_string_callback(|_, callback, user_data| unsafe {
|
wrap::nix_string_callback(|callback, user_data, _| unsafe {
|
||||||
sys::nix_store_path_name(self.inner.as_ptr(), Some(callback), user_data);
|
sys::nix_store_path_name(self.inner.as_ptr(), Some(callback), user_data);
|
||||||
|
|
||||||
// NOTE: nix_store_path_name doesn't return nix_err, so we force it to return successfully
|
// NOTE: nix_store_path_name doesn't return nix_err, so we force it to return successfully
|
||||||
nix_err_NIX_OK
|
nix_err_NIX_OK
|
||||||
})
|
})
|
||||||
|
|
@ -90,20 +85,14 @@ impl StorePath {
|
||||||
/// * `store` - The store containing the path
|
/// * `store` - The store containing the path
|
||||||
///
|
///
|
||||||
pub fn real_path(&self, store: &Store) -> Result<PathBuf, NixideError> {
|
pub fn real_path(&self, store: &Store) -> Result<PathBuf, NixideError> {
|
||||||
wrap_libnix_pathbuf_callback(|ctx, callback, user_data| unsafe {
|
wrap::nix_pathbuf_callback(|callback, user_data, ctx| unsafe {
|
||||||
let err_code = sys::nix_store_real_path(
|
sys::nix_store_real_path(
|
||||||
ctx.as_ptr(),
|
ctx.as_ptr(),
|
||||||
store.inner.as_ptr(),
|
store.inner.as_ptr(),
|
||||||
self.as_ptr(),
|
self.as_ptr(),
|
||||||
Some(callback),
|
Some(callback),
|
||||||
user_data,
|
user_data,
|
||||||
);
|
)
|
||||||
match ctx.pop() {
|
|
||||||
Some(err) => Err(err),
|
|
||||||
None => Ok(()),
|
|
||||||
}
|
|
||||||
|
|
||||||
err_code
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -115,45 +104,28 @@ impl StorePath {
|
||||||
/// * `store` - The store containing the path
|
/// * `store` - The store containing the path
|
||||||
///
|
///
|
||||||
pub fn is_valid(&self, store: &Store) -> bool {
|
pub fn is_valid(&self, store: &Store) -> bool {
|
||||||
let ctx = ErrorContext::new();
|
wrap::nix_fn!(|ctx: &ErrorContext| unsafe {
|
||||||
unsafe {
|
sys::nix_store_is_valid_path(ctx.as_ptr(), store.as_ptr(), self.as_ptr())
|
||||||
sys::nix_store_is_valid_path(ctx.as_ptr(), store.inner.as_ptr(), self.inner.as_ptr())
|
})
|
||||||
}
|
.is_ok()
|
||||||
|
|
||||||
ctx
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get the raw store path pointer.
|
|
||||||
///
|
|
||||||
/// # Safety
|
|
||||||
///
|
|
||||||
/// The caller must ensure the pointer is used safely.
|
|
||||||
pub(crate) unsafe fn as_ptr(&self) -> *mut sys::StorePath {
|
|
||||||
self.inner.as_ptr()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Clone for StorePath {
|
impl Clone for StorePath {
|
||||||
fn clone(&self) -> Self {
|
fn clone(&self) -> Self {
|
||||||
// SAFETY: self.inner is valid, nix_store_path_clone creates a new copy
|
let inner = wrap::nix_ptr_fn!(|_| unsafe { sys::nix_store_path_clone(self.as_ptr()) })
|
||||||
let cloned_ptr = unsafe { sys::nix_store_path_clone(self.inner.as_ptr()) };
|
.unwrap_or_else(|_| {
|
||||||
|
panic_issue_call_failed!("nix_store_path_clone returned None for valid path")
|
||||||
|
});
|
||||||
|
|
||||||
// This should never fail as cloning a valid path should always succeed
|
StorePath { inner }
|
||||||
let inner =
|
|
||||||
NonNull::new(cloned_ptr).expect("nix_store_path_clone returned null for valid path");
|
|
||||||
|
|
||||||
StorePath {
|
|
||||||
inner,
|
|
||||||
_context: Arc::clone(&self._context),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Drop for StorePath {
|
impl Drop for StorePath {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
// SAFETY: We own the store path and it's valid until drop
|
|
||||||
unsafe {
|
unsafe {
|
||||||
sys::nix_store_path_free(self.inner.as_ptr());
|
sys::nix_store_path_free(self.as_ptr());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,23 +5,17 @@ use super::*;
|
||||||
#[test]
|
#[test]
|
||||||
#[serial]
|
#[serial]
|
||||||
fn test_store_opening() {
|
fn test_store_opening() {
|
||||||
let ctx = Arc::new(ErrorContext::new().expect("Failed to create context"));
|
let _store = Store::open(None).expect("Failed to open store");
|
||||||
let _store = Store::open(&ctx, None).expect("Failed to open store");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[serial]
|
#[serial]
|
||||||
fn test_store_path_parse() {
|
fn test_store_path_parse() {
|
||||||
let ctx = Arc::new(ErrorContext::new().expect("Failed to create context"));
|
let store = Store::open(None).expect("Failed to open store");
|
||||||
let store = Store::open(&ctx, None).expect("Failed to open store");
|
|
||||||
|
|
||||||
// Try parsing a well-formed store path
|
// Try parsing a well-formed store path
|
||||||
// Note: This may fail if the path doesn't exist in the store
|
// Note: This may fail if the path doesn't exist in the store
|
||||||
let result = StorePath::parse(
|
let result = StorePath::parse(&store, "/nix/store/00000000000000000000000000000000-test");
|
||||||
&ctx,
|
|
||||||
&store,
|
|
||||||
"/nix/store/00000000000000000000000000000000-test",
|
|
||||||
);
|
|
||||||
|
|
||||||
// We don't assert success here because the path might not exist
|
// We don't assert success here because the path might not exist
|
||||||
// This test mainly checks that the API works correctly
|
// This test mainly checks that the API works correctly
|
||||||
|
|
@ -38,16 +32,11 @@ fn test_store_path_parse() {
|
||||||
#[test]
|
#[test]
|
||||||
#[serial]
|
#[serial]
|
||||||
fn test_store_path_clone() {
|
fn test_store_path_clone() {
|
||||||
let ctx = Arc::new(ErrorContext::new().expect("Failed to create context"));
|
let store = Store::open(None).expect("Failed to open store");
|
||||||
let store = Store::open(&ctx, None).expect("Failed to open store");
|
|
||||||
|
|
||||||
// Try to get a valid store path by parsing
|
// Try to get a valid store path by parsing
|
||||||
// Note: This test is somewhat limited without a guaranteed valid path
|
// Note: This test is somewhat limited without a guaranteed valid path
|
||||||
if let Ok(path) = StorePath::parse(
|
if let Ok(path) = StorePath::parse(&store, "/nix/store/00000000000000000000000000000000-test") {
|
||||||
&ctx,
|
|
||||||
&store,
|
|
||||||
"/nix/store/00000000000000000000000000000000-test",
|
|
||||||
) {
|
|
||||||
let cloned = path.clone();
|
let cloned = path.clone();
|
||||||
|
|
||||||
// Assert that the cloned path has the same name as the original
|
// Assert that the cloned path has the same name as the original
|
||||||
|
|
|
||||||
|
|
@ -1,47 +0,0 @@
|
||||||
use std::mem::MaybeUninit;
|
|
||||||
use std::os::raw::{c_char, c_uint, c_void};
|
|
||||||
use std::path::PathBuf;
|
|
||||||
|
|
||||||
use crate::errors::{ErrorContext, NixideError};
|
|
||||||
use crate::util::CCharPtrNixExt;
|
|
||||||
|
|
||||||
pub fn wrap_libnix_string_callback<F>(callback: F) -> Result<String, NixideError>
|
|
||||||
where
|
|
||||||
F: FnOnce(
|
|
||||||
&ErrorContext,
|
|
||||||
unsafe extern "C" fn(*const c_char, c_uint, *mut c_void),
|
|
||||||
*mut c_void,
|
|
||||||
) -> i32,
|
|
||||||
{
|
|
||||||
// Callback to receive the string
|
|
||||||
unsafe extern "C" fn wrapper_callback(start: *const c_char, n: c_uint, user_data: *mut c_void) {
|
|
||||||
let result = unsafe { &mut *(user_data as *mut Result<String, NixideError>) };
|
|
||||||
|
|
||||||
*result = start.to_utf8_string_sized(n as usize);
|
|
||||||
}
|
|
||||||
|
|
||||||
let ctx = ErrorContext::new();
|
|
||||||
let mut user_data: MaybeUninit<Result<String, NixideError>> = MaybeUninit::uninit();
|
|
||||||
|
|
||||||
callback(
|
|
||||||
&ctx,
|
|
||||||
wrapper_callback,
|
|
||||||
user_data.as_mut_ptr() as *mut c_void,
|
|
||||||
);
|
|
||||||
if let Some(err) = ctx.peak() {
|
|
||||||
return Err(err);
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe { user_data.assume_init() }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn wrap_libnix_pathbuf_callback<F>(callback: F) -> Result<PathBuf, NixideError>
|
|
||||||
where
|
|
||||||
F: FnOnce(
|
|
||||||
&ErrorContext,
|
|
||||||
unsafe extern "C" fn(*const c_char, c_uint, *mut c_void),
|
|
||||||
*mut c_void,
|
|
||||||
) -> i32,
|
|
||||||
{
|
|
||||||
wrap_libnix_string_callback(callback).map(PathBuf::from)
|
|
||||||
}
|
|
||||||
|
|
@ -1,23 +1,38 @@
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
pub mod panic;
|
pub mod panic;
|
||||||
pub(crate) mod bindings;
|
pub(crate) mod wrap;
|
||||||
mod cchar_nix_ext;
|
|
||||||
pub mod wrappers;
|
pub mod wrappers;
|
||||||
|
|
||||||
pub use cchar_nix_ext::CCharPtrNixExt;
|
|
||||||
pub(crate) use panic::*;
|
pub(crate) use panic::*;
|
||||||
|
|
||||||
use crate::NixideError;
|
// use crate::NixideError;
|
||||||
|
|
||||||
pub trait AsErr<T> {
|
// pub trait AsErr<T> {
|
||||||
fn as_err(self) -> Result<(), T>;
|
// fn as_err(self) -> Result<(), T>;
|
||||||
}
|
// }
|
||||||
|
|
||||||
impl AsErr<NixideError> for Option<NixideError> {
|
// impl AsErr<NixideError> for Option<NixideError> {
|
||||||
fn as_err(self) -> Result<(), NixideError> {
|
// fn as_err(self) -> Result<(), NixideError> {
|
||||||
match self {
|
// match self {
|
||||||
Some(err) => Err(err),
|
// Some(err) => Err(err),
|
||||||
None => Ok(()),
|
// None => Ok(()),
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
|
// pub trait AsInnerPtr<T> {
|
||||||
|
// /// Get a pointer to the underlying (`inner`) `libnix` C struct.
|
||||||
|
// ///
|
||||||
|
// /// # Safety
|
||||||
|
// ///
|
||||||
|
// /// Although this function isn't inherently `unsafe`, it is
|
||||||
|
// /// marked as such intentionally to force calls to be wrapped
|
||||||
|
// /// in `unsafe` blocks for clarity.
|
||||||
|
// unsafe fn as_ptr(&self) -> *mut T;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// pub trait FromC<T> {
|
||||||
|
// /// Creates a new instance of [Self] from the underlying
|
||||||
|
// /// libnix C type [T].
|
||||||
|
// unsafe fn from_c(value: T) -> Self;
|
||||||
|
// }
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,9 @@ 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", stdext::debug_name!())
|
||||||
}};
|
}};
|
||||||
|
($($arg:expr),*) => {{
|
||||||
|
crate::util::panic_issue!("[nixide] call to `{}` failed with \"{}\"", stdext::debug_name!(), format!($($arg),*))
|
||||||
|
}};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) use panic_issue;
|
pub(crate) use panic_issue;
|
||||||
|
|
|
||||||
147
nixide/src/util/wrap.rs
Normal file
147
nixide/src/util/wrap.rs
Normal file
|
|
@ -0,0 +1,147 @@
|
||||||
|
use std::mem::MaybeUninit;
|
||||||
|
use std::os::raw::{c_char, c_uint, c_void};
|
||||||
|
use std::path::PathBuf;
|
||||||
|
|
||||||
|
use crate::errors::{ErrorContext, NixideError};
|
||||||
|
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> {
|
||||||
|
unsafe fn as_ptr(&self) -> *mut T {
|
||||||
|
self.inner.as_mut_ptr()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! nonnull {
|
||||||
|
($ptr:expr $(,)? ) => {{
|
||||||
|
match ::std::ptr::NonNull::new($ptr) {
|
||||||
|
::std::option::Option::Some(p) => ::std::result::Result::Ok(p),
|
||||||
|
::std::option::Option::None => {
|
||||||
|
::std::result::Result::Err(crate::errors::new_nixide_error!(NullPtr))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}};
|
||||||
|
}
|
||||||
|
pub(crate) use nonnull;
|
||||||
|
|
||||||
|
macro_rules! nix_fn {
|
||||||
|
($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 result = $callback(
|
||||||
|
&ctx,
|
||||||
|
);
|
||||||
|
ctx.pop().and_then(|_| ::std::result::Result::Ok(result))
|
||||||
|
}};
|
||||||
|
}
|
||||||
|
pub(crate) use nix_fn;
|
||||||
|
|
||||||
|
macro_rules! nix_ptr_fn {
|
||||||
|
($callback:expr $(,)? ) => {{
|
||||||
|
crate::util::wrap::nix_fn!($callback).and_then(|ptr| crate::util::wrap::nonnull!(ptr))
|
||||||
|
}};
|
||||||
|
}
|
||||||
|
pub(crate) use nix_ptr_fn;
|
||||||
|
|
||||||
|
macro_rules! nix_callback {
|
||||||
|
( | $($arg_name:ident : $arg_type:ty),* ; userdata $( : *mut c_void )? $(,)? | -> $ret:ty $body:block, $callback:expr $(,)? ) => {{
|
||||||
|
// create a function item that wraps the closure body (so it has a concrete type)
|
||||||
|
#[allow(unused_variables)]
|
||||||
|
fn __captured_fn( $( $arg_name: $arg_type ),*, userdata: *mut ::std::os::raw::c_void) -> $ret $body
|
||||||
|
|
||||||
|
unsafe extern "C" fn __wrapper_callback(
|
||||||
|
$(
|
||||||
|
$arg_name: $arg_type,
|
||||||
|
)*
|
||||||
|
userdata: *mut ::std::os::raw::c_void,
|
||||||
|
) {
|
||||||
|
let result = unsafe { &mut *(userdata as *mut $ret) };
|
||||||
|
|
||||||
|
*result = __captured_fn(
|
||||||
|
$(
|
||||||
|
$arg_name,
|
||||||
|
)*
|
||||||
|
userdata,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 result: ::std::mem::MaybeUninit<$ret> = ::std::mem::MaybeUninit::uninit();
|
||||||
|
|
||||||
|
$callback(
|
||||||
|
__wrapper_callback,
|
||||||
|
result.as_mut_ptr() as *mut ::std::os::raw::c_void,
|
||||||
|
&ctx,
|
||||||
|
);
|
||||||
|
ctx.pop().and_then(|_| unsafe { result.assume_init() })
|
||||||
|
|
||||||
|
}};
|
||||||
|
|
||||||
|
( | userdata $( : *mut c_void )? ; $($arg_name:ident : $arg_type:ty),* | -> $ret:ty $body:block, $callback:expr $(,)? ) => {{
|
||||||
|
// create a function item that wraps the closure body (so it has a concrete type)
|
||||||
|
#[allow(unused_variables)]
|
||||||
|
fn __captured_fn(userdata: *mut ::std::os::raw::c_void, $($arg_name: $arg_type),*) -> $ret $body
|
||||||
|
|
||||||
|
unsafe extern "C" fn __wrapper_callback(
|
||||||
|
userdata: *mut ::std::os::raw::c_void,
|
||||||
|
$(
|
||||||
|
$arg_name: $arg_type,
|
||||||
|
)*
|
||||||
|
) {
|
||||||
|
let result = unsafe { &mut *(userdata as *mut $ret) };
|
||||||
|
|
||||||
|
*result = __captured_fn(
|
||||||
|
userdata,
|
||||||
|
$(
|
||||||
|
$arg_name,
|
||||||
|
)*
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 result: ::std::mem::MaybeUninit<$ret> = ::std::mem::MaybeUninit::uninit();
|
||||||
|
|
||||||
|
$callback(
|
||||||
|
__wrapper_callback,
|
||||||
|
result.as_mut_ptr() as *mut ::std::os::raw::c_void,
|
||||||
|
&ctx,
|
||||||
|
);
|
||||||
|
ctx.pop().and_then(|_| unsafe { result.assume_init() })
|
||||||
|
}};
|
||||||
|
}
|
||||||
|
pub(crate) use nix_callback;
|
||||||
|
|
||||||
|
pub fn nix_string_callback<F>(callback: F) -> Result<String, NixideError>
|
||||||
|
where
|
||||||
|
F: FnOnce(
|
||||||
|
unsafe extern "C" fn(*const c_char, c_uint, *mut c_void),
|
||||||
|
*mut c_void,
|
||||||
|
&ErrorContext,
|
||||||
|
) -> i32,
|
||||||
|
{
|
||||||
|
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 fn nix_pathbuf_callback<F>(callback: F) -> Result<PathBuf, NixideError>
|
||||||
|
where
|
||||||
|
F: FnOnce(
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
|
@ -1,3 +1,18 @@
|
||||||
|
use crate::NixideError;
|
||||||
|
|
||||||
|
pub trait AsErr<T> {
|
||||||
|
fn as_err(self) -> Result<(), T>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AsErr<NixideError> for Option<NixideError> {
|
||||||
|
fn as_err(self) -> Result<(), NixideError> {
|
||||||
|
match self {
|
||||||
|
Some(err) => Err(err),
|
||||||
|
None => Ok(()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub trait AsInnerPtr<T> {
|
pub trait AsInnerPtr<T> {
|
||||||
/// Get a pointer to the underlying (`inner`) `libnix` C struct.
|
/// Get a pointer to the underlying (`inner`) `libnix` C struct.
|
||||||
///
|
///
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue