The macro is similar to anyhow::ensure but uses QEMU's variation
on anyhow::Error. It can be used to easily check a condition
and format an error message.
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
rust/hw/timer/hpet/src/device.rs | 21 ++++++-----
rust/hw/timer/hpet/src/fw_cfg.rs | 7 ++--
rust/util/src/error.rs | 65 ++++++++++++++++++++++++++++++++
3 files changed, 80 insertions(+), 13 deletions(-)
diff --git a/rust/hw/timer/hpet/src/device.rs b/rust/hw/timer/hpet/src/device.rs
index 86638c07666..4218372a2a1 100644
--- a/rust/hw/timer/hpet/src/device.rs
+++ b/rust/hw/timer/hpet/src/device.rs
@@ -25,7 +25,10 @@
bindings::{address_space_memory, address_space_stl_le, hwaddr},
MemoryRegion, MemoryRegionOps, MemoryRegionOpsBuilder, MEMTXATTRS_UNSPECIFIED,
};
-use util::timer::{Timer, CLOCK_VIRTUAL, NANOSECONDS_PER_SECOND};
+use util::{
+ ensure,
+ timer::{Timer, CLOCK_VIRTUAL, NANOSECONDS_PER_SECOND},
+};
use crate::fw_cfg::HPETFwConfig;
@@ -728,14 +731,14 @@ fn post_init(&self) {
}
fn realize(&self) -> util::Result<()> {
- if self.num_timers < HPET_MIN_TIMERS || self.num_timers > HPET_MAX_TIMERS {
- Err(format!(
- "hpet.num_timers must be between {HPET_MIN_TIMERS} and {HPET_MAX_TIMERS}"
- ))?;
- }
- if self.int_route_cap == 0 {
- Err("hpet.hpet-intcap property not initialized")?;
- }
+ ensure!(
+ (HPET_MIN_TIMERS..=HPET_MAX_TIMERS).contains(&self.num_timers),
+ "hpet.num_timers must be between {HPET_MIN_TIMERS} and {HPET_MAX_TIMERS}"
+ );
+ ensure!(
+ self.int_route_cap != 0,
+ "hpet.hpet-intcap property not initialized"
+ );
self.hpet_id.set(HPETFwConfig::assign_hpet_id()?);
diff --git a/rust/hw/timer/hpet/src/fw_cfg.rs b/rust/hw/timer/hpet/src/fw_cfg.rs
index e569b57b93b..27b3b2495a0 100644
--- a/rust/hw/timer/hpet/src/fw_cfg.rs
+++ b/rust/hw/timer/hpet/src/fw_cfg.rs
@@ -5,6 +5,7 @@
use std::ptr::addr_of_mut;
use common::Zeroable;
+use util::{self, ensure};
/// Each `HPETState` represents a Event Timer Block. The v1 spec supports
/// up to 8 blocks. QEMU only uses 1 block (in PC machine).
@@ -36,7 +37,7 @@ unsafe impl Zeroable for HPETFwConfig {}
};
impl HPETFwConfig {
- pub(crate) fn assign_hpet_id() -> Result<usize, &'static str> {
+ pub(crate) fn assign_hpet_id() -> util::Result<usize> {
assert!(bql::is_locked());
// SAFETY: all accesses go through these methods, which guarantee
// that the accesses are protected by the BQL.
@@ -47,9 +48,7 @@ pub(crate) fn assign_hpet_id() -> Result<usize, &'static str> {
fw_cfg.count = 0;
}
- if fw_cfg.count == 8 {
- Err("Only 8 instances of HPET are allowed")?;
- }
+ ensure!(fw_cfg.count != 8, "Only 8 instances of HPET are allowed");
let id: usize = fw_cfg.count.into();
fw_cfg.count += 1;
diff --git a/rust/util/src/error.rs b/rust/util/src/error.rs
index bfa5a8685bc..20b8e7d5af5 100644
--- a/rust/util/src/error.rs
+++ b/rust/util/src/error.rs
@@ -86,6 +86,19 @@ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
}
}
+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(),
+ }
+ }
+}
+
impl From<String> for Error {
#[track_caller]
fn from(msg: String) -> Self {
@@ -126,6 +139,17 @@ fn from(error: anyhow::Error) -> Self {
}
impl Error {
+ #[track_caller]
+ #[doc(hidden)]
+ pub fn format(args: fmt::Arguments) -> Self {
+ if let Some(msg) = args.as_str() {
+ Self::from(msg)
+ } else {
+ let msg = std::fmt::format(args);
+ Self::from(msg)
+ }
+ }
+
/// Create a new error, prepending `msg` to the
/// description of `cause`
#[track_caller]
@@ -311,6 +335,47 @@ unsafe fn cloned_from_foreign(c_error: *const bindings::Error) -> Self {
}
}
+/// Ensure that a condition is true, returning an error if it is false.
+///
+/// This macro is similar to [`anyhow::ensure`] but returns a QEMU [`Result`].
+/// If the condition evaluates to `false`, the macro returns early with an error
+/// constructed from the provided message.
+///
+/// # Examples
+///
+/// ```
+/// # use util::{ensure, Result};
+/// # fn check_positive(x: i32) -> Result<()> {
+/// ensure!(x > 0, "value must be positive");
+/// # Ok(())
+/// # }
+/// ```
+///
+/// ```
+/// # use util::{ensure, Result};
+/// # const MIN: i32 = 123;
+/// # const MAX: i32 = 456;
+/// # fn check_range(x: i32) -> Result<()> {
+/// ensure!(x >= MIN && x <= MAX, "{} not between {} and {}", x, MIN, MAX);
+/// # Ok(())
+/// # }
+/// ```
+#[macro_export]
+macro_rules! ensure {
+ ($cond:expr, $fmt:literal, $($arg:tt)*) => {
+ if !$cond {
+ let e = $crate::Error::format(format_args!($fmt, $($arg)*));
+ return $crate::Result::Err(e);
+ }
+ };
+ ($cond:expr, $err:expr $(,)?) => {
+ if !$cond {
+ let s = ::std::borrow::Cow::<'static, str>::from($err);
+ return $crate::Result::Err(s.into());
+ }
+ };
+}
+
#[cfg(test)]
mod tests {
use std::ffi::CStr;
--
2.51.0