use core::str;
use std::{
error::Error as StdError,
ffi::{CStr, CString},
fmt::{self, Display},
mem::ManuallyDrop,
};
use super::{Vec, VecRef};
use crate::bindings::{_clib_error_return, CLIB_ERROR_ERRNO_VALID, clib_error_t, uword};
#[derive(Debug, Copy, Clone)]
#[repr(transparent)]
pub struct Error(clib_error_t);
impl fmt::Display for Error {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
if self.0.what.is_null() {
return write!(fmt, "<none>");
}
if !self.0.where_.is_null() {
let where_c = unsafe { CStr::from_ptr(self.0.where_.cast()) };
write!(fmt, "{}: ", where_c.to_string_lossy())?;
}
let what_v = unsafe { VecRef::from_raw(self.0.what) };
let what_str = str::from_utf8(what_v).unwrap_or("<invalid>");
write!(fmt, "{}", what_str)
}
}
#[derive(Debug)]
pub struct ErrorStack(Vec<Error>);
impl ErrorStack {
pub unsafe fn from_raw(ptr: *mut clib_error_t) -> Self {
unsafe { Self(Vec::from_raw(ptr.cast())) }
}
pub fn into_raw(self) -> *mut clib_error_t {
let errors = ManuallyDrop::new(self);
errors.0.as_mut_ptr().cast()
}
fn new_internal(
errors: Option<Self>,
what: String,
code: Option<crate::bindings::any>,
) -> Self {
let errors_ptr = if let Some(errors) = errors {
errors.into_raw()
} else {
std::ptr::null_mut()
};
let what_c = CString::new(what).expect("message should not contain nul characters");
let flags = if code.is_some() {
CLIB_ERROR_ERRNO_VALID as uword
} else {
0
};
let code = code.unwrap_or_default();
unsafe {
Self::from_raw(_clib_error_return(
errors_ptr,
code,
flags,
std::ptr::null_mut(),
what_c.as_ptr(),
))
}
}
pub fn new<E: StdError + Send + Sync + 'static>(e: E) -> Self {
Self::new_internal(None, e.to_string(), None)
}
pub fn msg<M>(message: M) -> Self
where
M: Display + Send + Sync + 'static,
{
Self::new_internal(None, message.to_string(), None)
}
pub fn context<C>(self, context: C) -> Self
where
C: Display + Send + Sync + 'static,
{
Self::new_internal(Some(self), context.to_string(), None)
}
pub fn errors(&self) -> &[Error] {
self.0.as_slice()
}
}
impl Drop for ErrorStack {
fn drop(&mut self) {
for e in self.errors() {
if e.0.what.is_null() {
continue;
}
let _ = unsafe { Vec::from_raw(e.0.what) };
}
}
}
impl Display for ErrorStack {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let errs = self.errors();
if let Some(err) = errs.last() {
write!(f, "{}", err)?;
if errs.len() > 1 {
write!(f, "\n\nCaused by:")?;
for err in errs[..errs.len() - 1].iter().rev() {
writeln!(f)?;
write!(f, " {}", err)?;
}
}
} else {
write!(f, "Empty VPP error")?;
}
Ok(())
}
}
impl StdError for ErrorStack {}
#[cfg(test)]
mod tests {
use super::*;
use crate::vppinfra::clib_mem_init;
#[test]
fn basic() {
clib_mem_init();
let io_e = std::io::Error::new(std::io::ErrorKind::AlreadyExists, "Already exists");
let e = ErrorStack::new(io_e);
assert_eq!(e.errors().len(), 1);
assert_eq!(e.to_string(), "Already exists");
let e = ErrorStack::msg("Unknown interface");
assert_eq!(e.errors().len(), 1);
assert_eq!(e.to_string(), "Unknown interface");
let e = e.context("Failed to enable feature");
assert_eq!(e.errors().len(), 2);
assert_eq!(
e.to_string(),
"\
Failed to enable feature
Caused by:
Unknown interface"
);
}
#[test]
fn ptrs() {
clib_mem_init();
let e = ErrorStack::msg("Unknown interface");
assert_eq!(e.errors().len(), 1);
assert_eq!(e.to_string(), "Unknown interface");
let ptr = e.into_raw();
let e = unsafe { ErrorStack::from_raw(ptr) };
assert_eq!(e.errors().len(), 1);
assert_eq!(e.to_string(), "Unknown interface");
}
}