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
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))
|
|
}
|
|
}
|