1use std::{ffi::CStr, fmt};
6
7use crate::sys;
8
9pub type Result<T> = std::result::Result<T, Error>;
11
12#[derive(Debug)]
14pub enum Error {
15 Unknown(String),
17
18 Overflow,
20
21 KeyNotFound(String),
23
24 IndexOutOfBounds {
26 index: usize,
28 length: usize,
30 },
31
32 EvalError(String),
34
35 InvalidType {
37 expected: &'static str,
39 actual: String,
41 },
42 NullPointer,
44
45 StringConversion(std::ffi::NulError),
47}
48
49impl fmt::Display for Error {
50 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
51 match self {
52 Error::Unknown(msg) => write!(f, "Unknown error: {msg}"),
53 Error::Overflow => write!(f, "Overflow error"),
54 Error::KeyNotFound(key) => write!(f, "Key not found: {key}"),
55 Error::IndexOutOfBounds { index, length } => {
56 write!(f, "Index out of bounds: index {index}, length {length}")
57 },
58 Error::EvalError(msg) => write!(f, "Evaluation error: {msg}"),
59 Error::InvalidType { expected, actual } => {
60 write!(f, "Invalid type: expected {expected}, got {actual}")
61 },
62 Error::NullPointer => write!(f, "Null pointer error"),
63 Error::StringConversion(e) => write!(f, "String conversion error: {e}"),
64 }
65 }
66}
67
68impl std::error::Error for Error {}
69
70impl From<std::ffi::NulError> for Error {
71 fn from(e: std::ffi::NulError) -> Self {
72 Error::StringConversion(e)
73 }
74}
75
76#[cfg(feature = "store")]
85pub(crate) unsafe fn string_from_callback<F>(call: F) -> Option<String>
86where
87 F: FnOnce(sys::nix_get_string_callback, *mut std::os::raw::c_void),
88{
89 let mut result: Option<String> = None;
90 let user_data = &mut result as *mut _ as *mut std::os::raw::c_void;
91 call(Some(collect_string), user_data);
92 result
93}
94
95#[cfg(feature = "store")]
102pub(crate) unsafe fn checked_string_from_callback<F>(
103 ctx: *mut sys::nix_c_context,
104 call: F,
105) -> Result<String>
106where
107 F: FnOnce(
108 sys::nix_get_string_callback,
109 *mut std::os::raw::c_void,
110 ) -> sys::nix_err,
111{
112 let mut result = None;
113 let user_data = &mut result as *mut _ as *mut std::os::raw::c_void;
114 let err = call(Some(collect_string), user_data);
115 check_err(ctx, err)?;
116 result.ok_or_else(|| {
117 Error::Unknown("Nix string callback returned no string".to_string())
118 })
119}
120
121#[cfg(feature = "store")]
122unsafe extern "C" fn collect_string(
123 start: *const std::os::raw::c_char,
124 n: std::os::raw::c_uint,
125 user_data: *mut std::os::raw::c_void,
126) {
127 let result = unsafe { &mut *(user_data as *mut Option<String>) };
128 if !start.is_null() {
129 let bytes =
130 unsafe { std::slice::from_raw_parts(start.cast::<u8>(), n as usize) };
131 *result = std::str::from_utf8(bytes).ok().map(str::to_owned);
132 }
133}
134
135#[cfg(feature = "store")]
138pub(crate) fn check_err(
139 ctx: *mut sys::nix_c_context,
140 err: sys::nix_err,
141) -> Result<()> {
142 if err == sys::nix_err_NIX_OK {
143 return Ok(());
144 }
145
146 let msg = unsafe {
150 let ptr = sys::nix_err_msg(std::ptr::null_mut(), ctx, std::ptr::null_mut());
151 if ptr.is_null() {
152 None
153 } else {
154 Some(CStr::from_ptr(ptr).to_string_lossy().into_owned())
155 }
156 };
157
158 let detail = if err == sys::nix_err_NIX_ERR_NIX_ERROR {
160 unsafe {
161 string_from_callback(|cb, ud| {
162 sys::nix_err_info_msg(std::ptr::null_mut(), ctx, cb, ud);
163 })
164 }
165 } else {
166 None
167 };
168
169 let name = unsafe {
173 string_from_callback(|cb, ud| {
174 sys::nix_err_name(std::ptr::null_mut(), ctx, cb, ud);
175 })
176 };
177
178 let base_message = detail
179 .or(msg)
180 .unwrap_or_else(|| format!("Nix error code: {err}"));
181 let message = match name {
182 Some(n) if !n.is_empty() => format!("[{n}] {base_message}"),
183 _ => base_message,
184 };
185
186 match err {
187 sys::nix_err_NIX_ERR_UNKNOWN => Err(Error::Unknown(message)),
188 sys::nix_err_NIX_ERR_OVERFLOW => Err(Error::Overflow),
189 sys::nix_err_NIX_ERR_KEY => Err(Error::KeyNotFound(message)),
190 sys::nix_err_NIX_ERR_NIX_ERROR => Err(Error::EvalError(message)),
191 _ => Err(Error::Unknown(message)),
192 }
193}
194
195#[cfg(feature = "store")]
211pub(crate) fn check_ptr<T>(
212 ctx: *mut sys::nix_c_context,
213 ptr: *mut T,
214) -> Result<std::ptr::NonNull<T>> {
215 if let Some(non_null) = std::ptr::NonNull::new(ptr) {
216 return Ok(non_null);
217 }
218 let code = unsafe { sys::nix_err_code(ctx) };
220 match check_err(ctx, code) {
221 Ok(()) => Err(Error::NullPointer),
222 Err(e) => Err(e),
223 }
224}