You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

151 lines
2.9 KiB

//! Errno handling
use super::*;
use std::{
error,
fmt,
};
use libc::__errno_location;
use once_cell::sync::OnceCell;
const MESSAGE_BUF_SIZE: usize = 512;
/// Get `errno` value.
#[inline] pub fn raw() -> i32
{
unsafe{*__errno_location()}
}
fn get_message(errno: i32) -> String
{
use libc::{
strerror_r,
};
use std::ffi::CStr;
let mut buffer = [0u8; MESSAGE_BUF_SIZE+1];
let cstr = unsafe {
strerror_r(errno, &mut buffer[0] as *mut u8 as *mut libc::c_char, MESSAGE_BUF_SIZE);
CStr::from_ptr(&buffer[0] as *const u8 as *const libc::c_char)
};
cstr.to_string_lossy().into_owned()
}
#[derive(Debug)]
/// Errno wrapper for Rust errors
pub struct Errno<T>
where T: fmt::Debug
{
errno: i32,
msg: OnceCell<String>,
pub internal: T,
}
impl<T> Errno<T>
where T: error::Error + 'static
{
/// Get the message for this errno
pub fn message(&self) -> &str
{
&self.msg.get_or_init(|| get_message(self.errno))[..]
}
/// Get the errno of this instance
pub fn error(&self) -> i32
{
self.errno
}
/// Consume this instance, returning the wrapped value
pub fn into_inner(self) -> T
{
self.internal
}
/// Map the inner value
pub fn map_inner<U,F>(self, fun: F) -> Errno<U>
where F: FnOnce(T) -> U,
U: error::Error + 'static
{
Errno {
internal: fun(self.internal),
errno: self.errno,
msg: self.msg,
}
}
/// Check if this `errno` value is 0 (`Success`)
pub fn is_success(&self) -> bool
{
self.errno == 0
}
}
impl<T> std::error::Error for Errno<T>
where T: error::Error + 'static
{
fn source(&self) -> Option<&(dyn error::Error + 'static)>
{
Some(&self.internal)
}
}
impl<T> std::fmt::Display for Errno<T>
where T: error::Error + 'static
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
{
write!(f, "{}: {} ({})", self.internal, self.message(), self.errno)
}
}
impl<T> Errno<T>
where T: error::Error + 'static
{
/// Create a new errno wrapper with specific `errno` value
pub fn with_errno(err: T, errno: i32) -> Self
{
Self {
errno,
msg: OnceCell::new(),
internal: err
}
}
/// Create a new errno wrapper with the current `errno` value
#[inline] pub fn new(err: T) -> Self
{
Self::with_errno(err, raw())
}
}
impl<T> From<T> for Errno<T>
where T: error::Error + 'static
{
#[inline] fn from(from: T) -> Self
{
Self::new(from)
}
}
pub trait ResultExt<T,V>
{
/// Call `map_inner()` on the inner error of `Errno<T>` if it is `Err`, otherwise return the same `Ok`.
fn map_inner<U,F>(self, fun: F) -> Result<V,Errno<U>>
where F: FnOnce(T) -> U,
U: error::Error + 'static;
}
impl<T,V> ResultExt<T,V> for Result<V,Errno<T>>
where T: error::Error + 'static
{
fn map_inner<U,F>(self, fun: F) -> Result<V,Errno<U>>
where F: FnOnce(T) -> U,
U: error::Error + 'static
{
self.map_err(|e| e.map_inner(fun))
}
}