diff options
Diffstat (limited to 'rust/qemu-api/src/error.rs')
| -rw-r--r-- | rust/qemu-api/src/error.rs | 416 |
1 files changed, 0 insertions, 416 deletions
diff --git a/rust/qemu-api/src/error.rs b/rust/qemu-api/src/error.rs deleted file mode 100644 index e114fc4178..0000000000 --- a/rust/qemu-api/src/error.rs +++ /dev/null @@ -1,416 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later - -//! Error propagation for QEMU Rust code -//! -//! This module contains [`Error`], the bridge between Rust errors and -//! [`Result`](std::result::Result)s and QEMU's C [`Error`](bindings::Error) -//! struct. -//! -//! For FFI code, [`Error`] provides functions to simplify conversion between -//! the Rust ([`Result<>`](std::result::Result)) and C (`Error**`) conventions: -//! -//! * [`ok_or_propagate`](crate::Error::ok_or_propagate), -//! [`bool_or_propagate`](crate::Error::bool_or_propagate), -//! [`ptr_or_propagate`](crate::Error::ptr_or_propagate) can be used to build -//! a C return value while also propagating an error condition -//! -//! * [`err_or_else`](crate::Error::err_or_else) and -//! [`err_or_unit`](crate::Error::err_or_unit) can be used to build a `Result` -//! -//! This module is most commonly used at the boundary between C and Rust code; -//! other code will usually access it through the -//! [`qemu_api::Result`](crate::Result) type alias, and will use the -//! [`std::error::Error`] interface to let C errors participate in Rust's error -//! handling functionality. -//! -//! Rust code can also create use this module to create an error object that -//! will be passed up to C code, though in most cases this will be done -//! transparently through the `?` operator. Errors can be constructed from a -//! simple error string, from an [`anyhow::Error`] to pass any other Rust error -//! type up to C code, or from a combination of the two. -//! -//! The third case, corresponding to [`Error::with_error`], is the only one that -//! requires mentioning [`qemu_api::Error`](crate::Error) explicitly. Similar -//! to how QEMU's C code handles errno values, the string and the -//! `anyhow::Error` object will be concatenated with `:` as the separator. - -use std::{ - borrow::Cow, - ffi::{c_char, c_int, c_void, CStr}, - fmt::{self, Display}, - panic, ptr, -}; - -use foreign::{prelude::*, OwnedPointer}; - -use crate::bindings; - -pub type Result<T> = std::result::Result<T, Error>; - -#[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>, - 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) - } - - #[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?") - } -} - -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<String> for Error { - #[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(), - } - } -} - -impl From<&'static str> for Error { - #[track_caller] - fn from(msg: &'static str) -> Self { - let location = panic::Location::caller(); - Error { - msg: Some(Cow::Borrowed(msg)), - cause: None, - file: location.file(), - line: location.line(), - } - } -} - -impl From<anyhow::Error> for Error { - #[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(), - } - } -} - -impl Error { - /// 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(), - } - } - - /// Consume a result, returning `false` if it is an error and - /// `true` if it is successful. The error is propagated into - /// `errp` like the C API `error_propagate` would do. - /// - /// # Safety - /// - /// `errp` must be a valid argument to `error_propagate`; - /// typically it is received from C code and need not be - /// checked further at the Rust↔C boundary. - pub unsafe fn bool_or_propagate(result: Result<()>, errp: *mut *mut bindings::Error) -> bool { - // SAFETY: caller guarantees errp is valid - unsafe { Self::ok_or_propagate(result, errp) }.is_some() - } - - /// Consume a result, returning a `NULL` pointer if it is an error and - /// a C representation of the contents if it is successful. This is - /// similar to the C API `error_propagate`, but it panics if `*errp` - /// is not `NULL`. - /// - /// # Safety - /// - /// `errp` must be a valid argument to `error_propagate`; - /// typically it is received from C code and need not be - /// checked further at the Rust↔C boundary. - /// - /// See [`propagate`](Error::propagate) for more information. - #[must_use] - pub unsafe fn ptr_or_propagate<T: CloneToForeign>( - result: Result<T>, - errp: *mut *mut bindings::Error, - ) -> *mut T::Foreign { - // SAFETY: caller guarantees errp is valid - unsafe { Self::ok_or_propagate(result, errp) }.clone_to_foreign_ptr() - } - - /// Consume a result in the same way as `self.ok()`, but also propagate - /// a possible error into `errp`. This is similar to the C API - /// `error_propagate`, but it panics if `*errp` is not `NULL`. - /// - /// # Safety - /// - /// `errp` must be a valid argument to `error_propagate`; - /// typically it is received from C code and need not be - /// checked further at the Rust↔C boundary. - /// - /// See [`propagate`](Error::propagate) for more information. - pub unsafe fn ok_or_propagate<T>( - result: Result<T>, - errp: *mut *mut bindings::Error, - ) -> Option<T> { - result.map_err(|err| unsafe { err.propagate(errp) }).ok() - } - - /// Equivalent of the C function `error_propagate`. Fill `*errp` - /// with the information container in `self` if `errp` is not NULL; - /// then consume it. - /// - /// This is similar to the C API `error_propagate`, but it panics if - /// `*errp` is not `NULL`. - /// - /// # Safety - /// - /// `errp` must be a valid argument to `error_propagate`; it can be - /// `NULL` or it can point to any of: - /// * `error_abort` - /// * `error_fatal` - /// * a local variable of (C) type `Error *` - /// - /// Typically `errp` is received from C code and need not be - /// checked further at the Rust↔C boundary. - pub unsafe fn propagate(self, errp: *mut *mut bindings::Error) { - if errp.is_null() { - return; - } - - // SAFETY: caller guarantees that errp and *errp are valid - unsafe { - assert_eq!(*errp, ptr::null_mut()); - bindings::error_propagate(errp, self.clone_to_foreign_ptr()); - } - } - - /// Convert a C `Error*` into a Rust `Result`, using - /// `Ok(())` if `c_error` is NULL. Free the `Error*`. - /// - /// # Safety - /// - /// `c_error` must be `NULL` or valid; typically it was initialized - /// with `ptr::null_mut()` and passed by reference to a C function. - pub unsafe fn err_or_unit(c_error: *mut bindings::Error) -> Result<()> { - // SAFETY: caller guarantees c_error is valid - unsafe { Self::err_or_else(c_error, || ()) } - } - - /// Convert a C `Error*` into a Rust `Result`, calling `f()` to - /// obtain an `Ok` value if `c_error` is NULL. Free the `Error*`. - /// - /// # Safety - /// - /// `c_error` must be `NULL` or point to a valid C [`struct - /// Error`](bindings::Error); typically it was initialized with - /// `ptr::null_mut()` and passed by reference to a C function. - pub unsafe fn err_or_else<T, F: FnOnce() -> T>( - c_error: *mut bindings::Error, - f: F, - ) -> Result<T> { - // SAFETY: caller guarantees c_error is valid - let err = unsafe { Option::<Self>::from_foreign(c_error) }; - match err { - None => Ok(f()), - Some(err) => Err(err), - } - } -} - -impl FreeForeign for Error { - type Foreign = bindings::Error; - - unsafe fn free_foreign(p: *mut bindings::Error) { - // SAFETY: caller guarantees p is valid - unsafe { - bindings::error_free(p); - } - } -} - -impl CloneToForeign for Error { - fn clone_to_foreign(&self) -> OwnedPointer<Self> { - // SAFETY: all arguments are controlled by this function - unsafe { - let err: *mut c_void = libc::malloc(std::mem::size_of::<bindings::Error>()); - let err: &mut bindings::Error = &mut *err.cast(); - *err = bindings::Error { - msg: format!("{self}").clone_to_foreign_ptr(), - err_class: bindings::ERROR_CLASS_GENERIC_ERROR, - src_len: self.file.len() as c_int, - src: self.file.as_ptr().cast::<c_char>(), - line: self.line as c_int, - func: ptr::null_mut(), - hint: ptr::null_mut(), - }; - OwnedPointer::new(err) - } - } -} - -impl FromForeign for Error { - unsafe fn cloned_from_foreign(c_error: *const bindings::Error) -> Self { - // SAFETY: caller guarantees c_error is valid - unsafe { - let error = &*c_error; - let file = if error.src_len < 0 { - // NUL-terminated - CStr::from_ptr(error.src).to_str() - } else { - // Can become str::from_utf8 with Rust 1.87.0 - std::str::from_utf8(std::slice::from_raw_parts( - &*error.src.cast::<u8>(), - error.src_len as usize, - )) - }; - - Error { - msg: FromForeign::cloned_from_foreign(error.msg), - cause: None, - file: file.unwrap(), - line: error.line as u32, - } - } - } -} - -#[cfg(test)] -mod tests { - use std::ffi::CStr; - - use anyhow::anyhow; - use foreign::OwnedPointer; - - use super::*; - use crate::{assert_match, bindings}; - - #[track_caller] - fn error_for_test(msg: &CStr) -> OwnedPointer<Error> { - // SAFETY: all arguments are controlled by this function - let location = panic::Location::caller(); - unsafe { - let err: *mut c_void = libc::malloc(std::mem::size_of::<bindings::Error>()); - let err: &mut bindings::Error = &mut *err.cast(); - *err = bindings::Error { - msg: msg.clone_to_foreign_ptr(), - err_class: bindings::ERROR_CLASS_GENERIC_ERROR, - src_len: location.file().len() as c_int, - src: location.file().as_ptr().cast::<c_char>(), - line: location.line() as c_int, - func: ptr::null_mut(), - hint: ptr::null_mut(), - }; - OwnedPointer::new(err) - } - } - - unsafe fn error_get_pretty<'a>(local_err: *mut bindings::Error) -> &'a CStr { - 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::from(anyhow!("msg"))), "msg"); - - assert_eq!( - &*format!("{}", Error::with_error("msg", anyhow!("cause"))), - "msg: cause" - ); - } - - #[test] - fn test_bool_or_propagate() { - unsafe { - let mut local_err: *mut bindings::Error = ptr::null_mut(); - - assert!(Error::bool_or_propagate(Ok(()), &mut local_err)); - assert_eq!(local_err, ptr::null_mut()); - - let my_err = Error::from("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"); - bindings::error_free(local_err); - } - } - - #[test] - fn test_ptr_or_propagate() { - unsafe { - let mut local_err: *mut bindings::Error = ptr::null_mut(); - - let ret = Error::ptr_or_propagate(Ok("abc".to_owned()), &mut local_err); - assert_eq!(String::from_foreign(ret), "abc"); - assert_eq!(local_err, ptr::null_mut()); - - let my_err = Error::from("msg"); - assert_eq!( - Error::ptr_or_propagate(Err::<String, _>(my_err), &mut local_err), - ptr::null_mut() - ); - assert_ne!(local_err, ptr::null_mut()); - assert_eq!(error_get_pretty(local_err), c"msg"); - bindings::error_free(local_err); - } - } - - #[test] - fn test_err_or_unit() { - unsafe { - let result = Error::err_or_unit(ptr::null_mut()); - assert_match!(result, Ok(())); - - let err = error_for_test(c"msg"); - let err = Error::err_or_unit(err.into_inner()).unwrap_err(); - assert_eq!(&*format!("{err}"), "msg"); - } - } -} |