From: Paolo Bonzini Date: Fri, 10 Oct 2025 12:25:08 +0000 (+0200) Subject: rust/util: add ensure macro X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=56dbf087a8a2cec7e3aeb19defed2a19efc85faa;p=thirdparty%2Fqemu.git rust/util: add ensure macro 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. Reviewed-by: Zhao Liu Signed-off-by: Paolo Bonzini --- diff --git a/rust/hw/timer/hpet/src/device.rs b/rust/hw/timer/hpet/src/device.rs index 23f2eefd1c..3564aa79c6 100644 --- a/rust/hw/timer/hpet/src/device.rs +++ b/rust/hw/timer/hpet/src/device.rs @@ -25,7 +25,10 @@ use system::{ 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 @@ impl HPETState { } 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 bb4ea8909a..777fc8ef45 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 @@ pub static mut hpet_fw_cfg: HPETFwConfig = HPETFwConfig { }; impl HPETFwConfig { - pub(crate) fn assign_hpet_id() -> Result { + pub(crate) fn assign_hpet_id() -> util::Result { 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 @@ impl HPETFwConfig { 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 bfa5a8685b..2a57c7fd5f 100644 --- a/rust/util/src/error.rs +++ b/rust/util/src/error.rs @@ -86,6 +86,19 @@ impl Display for Error { } } +impl From> 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 for Error { #[track_caller] fn from(msg: String) -> Self { @@ -126,6 +139,17 @@ impl From for Error { } 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 = fmt::format(args); + Self::from(msg) + } + } + /// Create a new error, prepending `msg` to the /// description of `cause` #[track_caller] @@ -311,6 +335,53 @@ impl FromForeign for Error { } } +/// 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;