use std::{ffi::CStr, fmt};
use crate::sys;
pub type Result<T> = std::result::Result<T, Error>;
#[derive(Debug)]
pub enum Error {
Unknown(String),
Overflow,
KeyNotFound(String),
IndexOutOfBounds {
index: usize,
length: usize,
},
EvalError(String),
InvalidType {
expected: &'static str,
actual: String,
},
NullPointer,
StringConversion(std::ffi::NulError),
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Error::Unknown(msg) => write!(f, "Unknown error: {msg}"),
Error::Overflow => write!(f, "Overflow error"),
Error::KeyNotFound(key) => write!(f, "Key not found: {key}"),
Error::IndexOutOfBounds { index, length } => {
write!(f, "Index out of bounds: index {index}, length {length}")
},
Error::EvalError(msg) => write!(f, "Evaluation error: {msg}"),
Error::InvalidType { expected, actual } => {
write!(f, "Invalid type: expected {expected}, got {actual}")
},
Error::NullPointer => write!(f, "Null pointer error"),
Error::StringConversion(e) => write!(f, "String conversion error: {e}"),
}
}
}
impl std::error::Error for Error {}
impl From<std::ffi::NulError> for Error {
fn from(e: std::ffi::NulError) -> Self {
Error::StringConversion(e)
}
}
#[cfg(feature = "store")]
pub(crate) unsafe fn string_from_callback<F>(call: F) -> Option<String>
where
F: FnOnce(sys::nix_get_string_callback, *mut std::os::raw::c_void),
{
unsafe extern "C" fn collect(
start: *const std::os::raw::c_char,
n: std::os::raw::c_uint,
user_data: *mut std::os::raw::c_void,
) {
let result = unsafe { &mut *(user_data as *mut Option<String>) };
if !start.is_null() {
let bytes =
unsafe { std::slice::from_raw_parts(start.cast::<u8>(), n as usize) };
*result = std::str::from_utf8(bytes).ok().map(|s| s.to_owned());
}
}
let mut result: Option<String> = None;
let user_data = &mut result as *mut _ as *mut std::os::raw::c_void;
call(Some(collect), user_data);
result
}
#[cfg(feature = "store")]
pub(crate) fn check_err(
ctx: *mut sys::nix_c_context,
err: sys::nix_err,
) -> Result<()> {
if err == sys::nix_err_NIX_OK {
return Ok(());
}
let msg = unsafe {
let ptr = sys::nix_err_msg(std::ptr::null_mut(), ctx, std::ptr::null_mut());
if ptr.is_null() {
None
} else {
Some(CStr::from_ptr(ptr).to_string_lossy().into_owned())
}
};
let detail = if err == sys::nix_err_NIX_ERR_NIX_ERROR {
unsafe {
string_from_callback(|cb, ud| {
sys::nix_err_info_msg(std::ptr::null_mut(), ctx, cb, ud);
})
}
} else {
None
};
let name = unsafe {
string_from_callback(|cb, ud| {
sys::nix_err_name(std::ptr::null_mut(), ctx, cb, ud);
})
};
let base_message = detail
.or(msg)
.unwrap_or_else(|| format!("Nix error code: {err}"));
let message = match name {
Some(n) if !n.is_empty() => format!("[{n}] {base_message}"),
_ => base_message,
};
match err {
sys::nix_err_NIX_ERR_UNKNOWN => Err(Error::Unknown(message)),
sys::nix_err_NIX_ERR_OVERFLOW => Err(Error::Overflow),
sys::nix_err_NIX_ERR_KEY => Err(Error::KeyNotFound(message)),
sys::nix_err_NIX_ERR_NIX_ERROR => Err(Error::EvalError(message)),
_ => Err(Error::Unknown(message)),
}
}