[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[PATCH 05/14] rust: define wrappers for Error
From: |
Paolo Bonzini |
Subject: |
[PATCH 05/14] rust: define wrappers for Error |
Date: |
Mon, 1 Jul 2024 16:58:37 +0200 |
The wrappers for Error provide the following functionality:
- a struct that implements std::error::Error and can be filled with
information similar to what would go into a C Error* (location, message).
- functionality similar to error_prepend() via the "cause()"
member of std::error::Error.
- converting a Result<> into a value that can be returned to C,
filling in an Error** (like error_propagate() would do) if the
Result<> contains an error; useful for callbacks written in Rust
- converting a C Error* into a Result that can be returned to Rust,
useful for Rust wrappers of C functions
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
qemu/src/lib.rs | 2 +
qemu/src/util/error.rs | 241 +++++++++++++++++++++++++++++++++++++++++
qemu/src/util/mod.rs | 1 +
3 files changed, 244 insertions(+)
create mode 100644 qemu/src/util/error.rs
diff --git a/qemu/src/lib.rs b/qemu/src/lib.rs
index c48edcf..5f926b8 100644
--- a/qemu/src/lib.rs
+++ b/qemu/src/lib.rs
@@ -4,7 +4,9 @@
pub mod bindings;
pub mod util;
+pub use util::error::Error;
pub use util::foreign::CloneToForeign;
pub use util::foreign::FromForeign;
pub use util::foreign::IntoNative;
pub use util::foreign::OwnedPointer;
+pub type Result<T> = std::result::Result<T, Error>;
diff --git a/qemu/src/util/error.rs b/qemu/src/util/error.rs
new file mode 100644
index 0000000..e7e6f2e
--- /dev/null
+++ b/qemu/src/util/error.rs
@@ -0,0 +1,241 @@
+//! Error class for QEMU Rust code
+//!
+//! @author Paolo Bonzini
+
+use crate::bindings;
+use crate::bindings::error_free;
+use crate::bindings::error_get_pretty;
+use crate::bindings::error_setg_internal;
+
+use std::ffi::CStr;
+use std::fmt::{self, Display};
+use std::ptr;
+
+use crate::util::foreign::{CloneToForeign, FromForeign, OwnedPointer};
+
+#[derive(Debug, Default)]
+pub struct Error {
+ msg: Option<String>,
+ /// Appends the print string of the error to the msg if not None
+ cause: Option<Box<dyn std::error::Error>>,
+ location: Option<(String, u32)>,
+}
+
+impl std::error::Error for Error {
+ fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
+ self.cause.as_deref()
+ }
+
+ #[allow(deprecated)]
+ fn description(&self) -> &str {
+ self.msg
+ .as_deref()
+ .or_else(||
self.cause.as_deref().map(std::error::Error::description))
+ .unwrap_or("error")
+ }
+}
+
+impl Display for Error {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ let mut prefix = "";
+ if let Some((ref file, line)) = self.location {
+ write!(f, "{}:{}", file, line)?;
+ prefix = ": ";
+ }
+ if let Some(ref msg) = self.msg {
+ write!(f, "{}{}", prefix, msg)?;
+ prefix = ": ";
+ }
+ if let Some(ref cause) = self.cause {
+ write!(f, "{}{}", prefix, cause)?;
+ } else if prefix.is_empty() {
+ f.write_str("unknown error")?;
+ }
+ Ok(())
+ }
+}
+
+impl From<&str> for Error {
+ fn from(msg: &str) -> Self {
+ Error {
+ msg: Some(String::from(msg)),
+ cause: None,
+ location: None,
+ }
+ }
+}
+
+impl From<std::io::Error> for Error {
+ fn from(error: std::io::Error) -> Self {
+ Error {
+ msg: None,
+ cause: Some(Box::new(error)),
+ location: None,
+ }
+ }
+}
+
+impl Error {
+ /// Create a new error, prepending `msg` to the
+ /// description of `cause`
+ pub fn with_error<E: std::error::Error + 'static>(msg: &str, cause: E) ->
Self {
+ Error {
+ msg: Some(String::from(msg)),
+ cause: Some(Box::new(cause)),
+ location: None,
+ }
+ }
+
+ /// Create a new error, prepending `file:line: msg` to the
+ /// description of `cause`
+ pub fn with_error_file_line<E: std::error::Error + 'static>(
+ msg: &str,
+ cause: E,
+ file: &str,
+ line: u32,
+ ) -> Self {
+ Error {
+ msg: Some(String::from(msg)),
+ cause: Some(Box::new(cause)),
+ location: Some((String::from(file), line)),
+ }
+ }
+
+ /// Create a new error with format `file:line: msg`
+ pub fn with_file_line(msg: &str, file: &str, line: u32) -> Self {
+ Error {
+ msg: Some(String::from(msg)),
+ cause: None,
+ location: Some((String::from(file), 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 valid; typically it is received from C code
+ pub unsafe fn bool_or_propagate<T>(
+ result: Result<(), Self>,
+ errp: *mut *mut bindings::Error,
+ ) -> bool {
+ 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. The error is propagated into `errp` like
+ /// the C API `error_propagate` would do.
+ ///
+ /// # Safety
+ ///
+ /// `errp` must be valid; typically it is received from C code
+ pub unsafe fn ptr_or_propagate<T: CloneToForeign>(
+ result: Result<T, Self>,
+ errp: *mut *mut bindings::Error,
+ ) -> *mut T::Foreign {
+ Self::ok_or_propagate(result, errp).map_or(ptr::null_mut(), |ref x| {
+ CloneToForeign::clone_to_foreign_ptr(x)
+ })
+ }
+
+ /// Consume a result and return `self.ok()`, but also propagate a
+ /// possible error into `errp`, like the C API `error_propagate`
+ /// would do.
+ ///
+ /// # Safety
+ ///
+ /// `errp` must be valid; typically it is received from C code
+ pub unsafe fn ok_or_propagate<T>(
+ result: Result<T, Self>,
+ errp: *mut *mut bindings::Error,
+ ) -> Option<T> {
+ match result {
+ Ok(ok) => Some(ok),
+ Err(err) => {
+ err.propagate(errp);
+ None
+ }
+ }
+ }
+
+ /// Equivalent of the C function `error_propagate`. Fill `*errp`
+ /// with the information container in `self` if `errp` is not NULL;
+ /// then consume it.
+ ///
+ /// # Safety
+ ///
+ /// `errp` must be valid; typically it is received from C code
+ pub unsafe fn propagate(self, errp: *mut *mut bindings::Error) {
+ if errp.is_null() {
+ return;
+ }
+ errp.write(self.clone_to_foreign_ptr());
+ }
+
+ /// Convert a C `Error*` into a Rust `Result`, using
`Ok(Default::default())`
+ /// if `c_error` is NULL.
+ ///
+ /// # Safety
+ ///
+ /// `c_error` must be valid; typically it has been filled by a C
+ /// function.
+ pub unsafe fn err_or_default<T: Default>(c_error: *mut bindings::Error) ->
Result<T, Self> {
+ Self::err_or_else(c_error, Default::default)
+ }
+
+ /// Convert a C `Error*` into a Rust `Result`, calling `f()` to
+ /// obtain an `Ok` value if `c_error` is NULL.
+ ///
+ /// # Safety
+ ///
+ /// `c_error` must be valid; typically it has been filled by a C
+ /// function.
+ pub unsafe fn err_or_else<T, F: FnOnce() -> T>(
+ c_error: *mut bindings::Error,
+ f: F,
+ ) -> Result<T, Self> {
+ match Option::<Self>::from_foreign(c_error) {
+ None => Ok(f()),
+ Some(err) => Err(err),
+ }
+ }
+}
+
+impl CloneToForeign for Error {
+ type Foreign = bindings::Error;
+
+ fn clone_to_foreign(&self) -> OwnedPointer<Self> {
+ let mut x: *mut bindings::Error = ptr::null_mut();
+ unsafe {
+ error_setg_internal(
+ &mut x,
+ ptr::null_mut(), // FIXME
+ 0,
+ ptr::null_mut(), // FIXME
+ c"%s".as_ptr(),
+ format!("{}", self),
+ );
+ OwnedPointer::new(x)
+ }
+ }
+
+ unsafe fn free_foreign(p: *mut bindings::Error) {
+ unsafe {
+ error_free(p);
+ }
+ }
+}
+
+impl FromForeign for Error {
+ unsafe fn cloned_from_foreign(c_error: *const bindings::Error) -> Self {
+ let c_str = unsafe { CStr::from_ptr(error_get_pretty(c_error)) };
+ Error {
+ msg: Some(c_str.to_string_lossy().into_owned()),
+ cause: None,
+ location: None,
+ }
+ }
+}
diff --git a/qemu/src/util/mod.rs b/qemu/src/util/mod.rs
index be00d0c..e6078ac 100644
--- a/qemu/src/util/mod.rs
+++ b/qemu/src/util/mod.rs
@@ -1 +1,2 @@
+pub mod error;
pub mod foreign;
--
2.45.2
- [PATCH 00/14] rust: example of bindings code for Rust in QEMU, Paolo Bonzini, 2024/07/01
- [PATCH 01/14] add skeleton, Paolo Bonzini, 2024/07/01
- [PATCH 02/14] set expectations, Paolo Bonzini, 2024/07/01
- [PATCH 04/14] rust: add tests for util::foreign, Paolo Bonzini, 2024/07/01
- [PATCH 05/14] rust: define wrappers for Error,
Paolo Bonzini <=
- [PATCH 03/14] rust: define traits and pointer wrappers to convert from/to C representations, Paolo Bonzini, 2024/07/01
- [PATCH 07/14] rust: define wrappers for methods of the QOM Object class, Paolo Bonzini, 2024/07/01
- [PATCH 08/14] rust: define wrappers for methods of the QOM Device class, Paolo Bonzini, 2024/07/01
- [PATCH 06/14] rust: define wrappers for basic QOM concepts, Paolo Bonzini, 2024/07/01
- [PATCH 09/14] rust: add idiomatic bindings to define Object subclasses, Paolo Bonzini, 2024/07/01
- [PATCH 10/14] rust: add idiomatic bindings to define Device subclasses, Paolo Bonzini, 2024/07/01
- [PATCH 12/14] rust: replace c"" literals with cstr crate, Paolo Bonzini, 2024/07/01