//! Handling C `errno` error values in Rust use super::*; use std::{ fmt, error, }; use libc::c_char; use libc::__errno_location; use once_cell::sync::OnceCell; use std::num::NonZeroI32; /// Size of errno string message buffer const MESSAGE_BUF_SIZE: usize = 512; /// A raw errno error code. pub type RawError = i32; /// Get the raw errno error value. #[inline] pub fn raw() -> RawError { unsafe { *__errno_location() } } /// Convert raw error code into its message fn get_message(raw: RawError) -> String { use libc::strerror_r; use std::ffi::CStr; let mut buffer = [0u8; MESSAGE_BUF_SIZE+1]; let cstr = unsafe { strerror_r(raw, buffer.as_mut_ptr() as *mut c_char, MESSAGE_BUF_SIZE); debug_if!(if { buffer[MESSAGE_BUF_SIZE] = 0; }); CStr::from_ptr(buffer.as_mut_ptr() as *const c_char) }; cstr.to_string_lossy().into_owned() } /// A raw C error #[derive(Debug)] pub struct Errno { errno: NonZeroI32, message: OnceCell, } impl Errno { /// Get the raw errno value of this error. pub const fn raw(&self) -> i32 { self.errno.get() } /// Get the errno message for this error pub fn message(&self) -> &str { &self.message.get_or_init(|| get_message(self.errno.into())) } /// Create a new error wrapper for this errno value without checking if it was success. /// /// # Safety /// The caller **must** verify that `raw` is not 0 (success) or memory corruption may be possible. pub const unsafe fn new_unchecked(raw: RawError) -> Self { Self{errno: NonZeroI32::new_unchecked(raw), message: OnceCell::new()} } /// Create a new error wrapper for this errno value if it was not success. pub const fn new(raw: RawError) -> Result<(), Self> { if raw == 0 { Ok(()) } else { Err(unsafe {Self::new_unchecked(raw)}) } } /// Create a new error wrapper for the last errno value set on this thread if it was not success. pub fn last() -> Result<(), Self> { Self::new(raw()) } /// Create a new `Result` that returns the last errno value set if it was an error, or calls this function and returns the value returned from it if it was success. pub fn or_last_with(fun: F) -> Result where F: FnOnce() -> T { Self::last().map(move |_| fun()) } /// Create a new `Result` that returns the last errno value set if it was an error, or returns the provided value if it was success. #[inline] pub fn or_last(val: T) -> Result { Self::or_last_with(move || val) } /// Create a new `Result` that returns the this errno value if it was an error, or calls this function and returns the value returned from it if it was success. pub fn or_new_with(raw: RawError, fun: F) -> Result where F: FnOnce() -> T { Self::new(raw).map(move |_| fun()) } /// Create a new `Result` that returns this errno value if it was an error, or returns the provided value if it was success. #[inline] pub fn or_new(raw: RawError, val: T) -> Result { Self::or_new_with(raw, move || val) } } impl error::Error for Errno{} impl fmt::Display for Errno { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}", self.message()) } }