borrow::Cow,
ffi::{c_char, c_int, c_void, CStr},
fmt::{self, Display},
+ ops::Deref,
panic, ptr,
};
#[derive(Debug)]
pub struct Error {
- msg: Option<Cow<'static, str>>,
- /// Appends the print string of the error to the msg if not None
- cause: Option<anyhow::Error>,
+ cause: anyhow::Error,
file: &'static str,
line: u32,
}
-impl std::error::Error for Error {
- fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
- self.cause.as_ref().map(AsRef::as_ref)
- }
+impl Deref for Error {
+ type Target = anyhow::Error;
- #[allow(deprecated)]
- fn description(&self) -> &str {
- self.msg
- .as_deref()
- .or_else(|| self.cause.as_deref().map(std::error::Error::description))
- .expect("no message nor cause?")
+ fn deref(&self) -> &Self::Target {
+ &self.cause
}
}
impl Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- let mut prefix = "";
- if let Some(ref msg) = self.msg {
- write!(f, "{msg}")?;
- prefix = ": ";
- }
- if let Some(ref cause) = self.cause {
- write!(f, "{prefix}{cause}")?;
- } else if prefix.is_empty() {
- panic!("no message nor cause?");
- }
- Ok(())
- }
-}
-
-impl From<Cow<'static, str>> for Error {
- #[track_caller]
- fn from(msg: Cow<'static, str>) -> Self {
- let location = panic::Location::caller();
- Error {
- msg: Some(msg),
- cause: None,
- file: location.file(),
- line: location.line(),
- }
+ Display::fmt(&format_args!("{:#}", self.cause), f)
}
}
-impl From<String> for Error {
+impl<E> From<E> for Error
+where
+ anyhow::Error: From<E>,
+{
#[track_caller]
- fn from(msg: String) -> Self {
- let location = panic::Location::caller();
- Error {
- msg: Some(Cow::Owned(msg)),
- cause: None,
- file: location.file(),
- line: location.line(),
- }
+ fn from(src: E) -> Self {
+ Self::new(anyhow::Error::from(src))
}
}
-impl From<&'static str> for Error {
+impl Error {
+ /// Create a new error from an [`anyhow::Error`].
+ ///
+ /// This wraps the error with QEMU's location tracking information.
+ /// Most code should use the `?` operator instead of calling this directly.
#[track_caller]
- fn from(msg: &'static str) -> Self {
+ pub fn new(cause: anyhow::Error) -> Self {
let location = panic::Location::caller();
- Error {
- msg: Some(Cow::Borrowed(msg)),
- cause: None,
+ Self {
+ cause,
file: location.file(),
line: location.line(),
}
}
-}
-impl From<anyhow::Error> for Error {
+ /// Create a new error from a string message.
+ ///
+ /// This is a convenience wrapper around [`Error::new`] for simple string
+ /// errors. Most code should use the [`ensure!`](crate::ensure) macro
+ /// instead of calling this directly.
#[track_caller]
- fn from(error: anyhow::Error) -> Self {
- let location = panic::Location::caller();
- Error {
- msg: None,
- cause: Some(error),
- file: location.file(),
- line: location.line(),
- }
+ pub fn msg(src: impl Into<Cow<'static, str>>) -> Self {
+ Self::new(anyhow::Error::msg(src.into()))
}
-}
-impl Error {
#[track_caller]
#[doc(hidden)]
+ #[inline(always)]
pub fn format(args: fmt::Arguments) -> Self {
- if let Some(msg) = args.as_str() {
- Self::from(msg)
- } else {
- let msg = fmt::format(args);
- Self::from(msg)
- }
+ // anyhow::Error::msg will allocate anyway, might as well let fmt::format doit.
+ let msg = fmt::format(args);
+ Self::new(anyhow::Error::msg(msg))
}
/// Create a new error, prepending `msg` to the
/// description of `cause`
#[track_caller]
pub fn with_error(msg: impl Into<Cow<'static, str>>, cause: impl Into<anyhow::Error>) -> Self {
- let location = panic::Location::caller();
- Error {
- msg: Some(msg.into()),
- cause: Some(cause.into()),
- file: location.file(),
- line: location.line(),
+ fn do_with_error(
+ msg: Cow<'static, str>,
+ cause: anyhow::Error,
+ location: &'static panic::Location<'static>,
+ ) -> Error {
+ Error {
+ cause: cause.context(msg),
+ file: location.file(),
+ line: location.line(),
+ }
}
+ do_with_error(msg.into(), cause.into(), panic::Location::caller())
}
/// Consume a result, returning `false` if it is an error and
};
Error {
- msg: FromForeign::cloned_from_foreign(error.msg),
- cause: None,
+ cause: anyhow::Error::msg(String::cloned_from_foreign(error.msg)),
file: file.unwrap(),
line: error.line as u32,
}
};
($cond:expr, $err:expr $(,)?) => {
if !$cond {
- let s = ::std::borrow::Cow::<'static, str>::from($err);
- return $crate::Result::Err(s.into());
+ let e = $crate::Error::msg($err);
+ return $crate::Result::Err(e);
}
};
}
unsafe { CStr::from_ptr(bindings::error_get_pretty(local_err)) }
}
- #[test]
- #[allow(deprecated)]
- fn test_description() {
- use std::error::Error;
-
- assert_eq!(super::Error::from("msg").description(), "msg");
- assert_eq!(super::Error::from("msg".to_owned()).description(), "msg");
- }
-
#[test]
fn test_display() {
- assert_eq!(&*format!("{}", Error::from("msg")), "msg");
- assert_eq!(&*format!("{}", Error::from("msg".to_owned())), "msg");
+ assert_eq!(&*format!("{}", Error::msg("msg")), "msg");
+ assert_eq!(&*format!("{}", Error::msg("msg".to_owned())), "msg");
assert_eq!(&*format!("{}", Error::from(anyhow!("msg"))), "msg");
assert_eq!(
assert!(Error::bool_or_propagate(Ok(()), &mut local_err));
assert_eq!(local_err, ptr::null_mut());
- let my_err = Error::from("msg");
+ let my_err = Error::msg("msg");
assert!(!Error::bool_or_propagate(Err(my_err), &mut local_err));
assert_ne!(local_err, ptr::null_mut());
assert_eq!(error_get_pretty(local_err), c"msg");
assert_eq!(String::from_foreign(ret), "abc");
assert_eq!(local_err, ptr::null_mut());
- let my_err = Error::from("msg");
+ let my_err = Error::msg("msg");
assert_eq!(
Error::ptr_or_propagate(Err::<String, _>(my_err), &mut local_err),
ptr::null_mut()