From 7c8a7b4ea56969a8931f39dd2b2ef889ffdb9ac6 Mon Sep 17 00:00:00 2001 From: luojia65 Date: Thu, 16 Feb 2023 17:40:53 +0800 Subject: [PATCH] doc: `Result`-like documents to `SbiRet` --- CHANGELOG.md | 1 + src/binary.rs | 334 +++++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 333 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 51cf21b..be058f6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), - Adapt to RISC-V SBI specification version 2.0-rc1 - `PMU_COUNTER_FW_READ_HIGH` function in `pmu` module for RV32 systems - SBI DBCN extension support +- `Result`-like documents to `SbiRet` ### Modified diff --git a/src/binary.rs b/src/binary.rs index 7b2c73a..e8bd49a 100644 --- a/src/binary.rs +++ b/src/binary.rs @@ -119,6 +119,19 @@ impl SbiRet { value: 0, } } + /// SBI call failed due to denied. + /// + /// As the time this document was written, + /// there is currently no function in SBI standard that returns this error. + /// However, custom extensions or future standard functions may return this + /// error if appropriate. + #[inline] + pub const fn denied() -> Self { + Self { + error: RET_ERR_DENIED, + value: 0, + } + } /// SBI call failed for invalid mask start address, /// not a valid physical address parameter, @@ -181,6 +194,19 @@ impl SbiRet { } /// Returns `true` if current SBI return succeeded. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// # use sbi_spec::binary::SbiRet; + /// let x = SbiRet::success(0); + /// assert_eq!(x.is_ok(), true); + /// + /// let x = SbiRet::failed(); + /// assert_eq!(x.is_ok(), false); + /// ``` #[must_use = "if you intended to assert that this is ok, consider `.unwrap()` instead"] #[inline] pub const fn is_ok(&self) -> bool { @@ -188,6 +214,19 @@ impl SbiRet { } /// Returns `true` if current SBI return is an error. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// # use sbi_spec::binary::SbiRet; + /// let x = SbiRet::success(0); + /// assert_eq!(x.is_err(), false); + /// + /// let x = SbiRet::not_supported(); + /// assert_eq!(x.is_err(), true); + /// ``` #[must_use = "if you intended to assert that this is err, consider `.unwrap_err()` instead"] #[inline] pub const fn is_err(&self) -> bool { @@ -195,6 +234,22 @@ impl SbiRet { } /// Converts from `SbiRet` to [`Option`]. + /// + /// Converts `self` into an [`Option`], consuming `self`, + /// and discarding the error, if any. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// # use sbi_spec::binary::SbiRet; + /// let x = SbiRet::success(2); + /// assert_eq!(x.ok(), Some(2)); + /// + /// let x = SbiRet::invalid_param(); + /// assert_eq!(x.ok(), None); + /// ``` // fixme: should be pub const fn once this function in Result is stablized in constant #[inline] pub fn ok(self) -> Option { @@ -202,21 +257,80 @@ impl SbiRet { } /// Converts from `SbiRet` to [`Option`]. + /// + /// Converts `self` into an [`Option`], consuming `self`, + /// and discarding the success value, if any. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// # use sbi_spec::binary::{SbiRet, Error}; + /// let x = SbiRet::success(2); + /// assert_eq!(x.err(), None); + /// + /// let x = SbiRet::denied(); + /// assert_eq!(x.err(), Some(Error::Denied)); + /// ``` // fixme: should be pub const fn once this function in Result is stablized in constant #[inline] pub fn err(self) -> Option { self.into_result().err() } - /// Maps a `SbiRet` to `Result` by applying a function to a + /// Maps a `SbiRet` to `Result` by applying a function to a /// contained success value, leaving an error value untouched. + /// + /// This function can be used to compose the results of two functions. + /// + /// # Examples + /// + /// Gets detail of a PMU counter and judge if it is a firmware counter. + /// + /// ``` + /// # use sbi_spec::binary::SbiRet; + /// # use core::mem::size_of; + /// # mod sbi_rt { + /// # use sbi_spec::binary::SbiRet; + /// # const TYPE_MASK: usize = 1 << (core::mem::size_of::() - 1); + /// # pub fn pmu_counter_get_info(_: usize) -> SbiRet { SbiRet::success(TYPE_MASK) } + /// # } + /// // We assume that counter index 42 is a firmware counter. + /// let counter_idx = 42; + /// // Masks PMU counter type by setting highest bit in `usize`. + /// const TYPE_MASK: usize = 1 << (size_of::() - 1); + /// // Highest bit of returned `counter_info` represents whether it's + /// // a firmware counter or a hardware counter. + /// let is_firmware_counter = sbi_rt::pmu_counter_get_info(counter_idx) + /// .map(|counter_info| counter_info & TYPE_MASK != 0); + /// // If that bit is set, it is a firmware counter. + /// assert_eq!(is_firmware_counter, Ok(true)); + /// ``` #[inline] pub fn map U>(self, op: F) -> Result { self.into_result().map(op) } /// Returns the provided default (if error), or - /// applies a function to the contained value (if success), + /// applies a function to the contained value (if success). + /// + /// Arguments passed to `map_or` are eagerly evaluated; if you are passing + /// the result of a function call, it is recommended to use [`map_or_else`], + /// which is lazily evaluated. + /// + /// [`map_or_else`]: SbiRet::map_or_else + /// + /// # Examples + /// + /// ``` + /// # use sbi_spec::binary::SbiRet; + /// let x = SbiRet::success(3); + /// assert_eq!(x.map_or(42, |v| v & 0b1), 1); + /// + /// let x = SbiRet::invalid_address(); + /// assert_eq!(x.map_or(42, |v| v & 0b1), 42); + /// ``` #[inline] pub fn map_or U>(self, default: U, f: F) -> U { self.into_result().map_or(default, f) @@ -224,6 +338,25 @@ impl SbiRet { /// Maps a `SbiRet` to `usize` value by applying fallback function `default` to /// a contained error, or function `f` to a contained success value. + /// + /// This function can be used to unpack a successful result + /// while handling an error. + /// + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// # use sbi_spec::binary::SbiRet; + /// let k = 21; + /// + /// let x = SbiRet::success(3); + /// assert_eq!(x.map_or_else(|e| k * 2, |v| v & 0b1), 1); + /// + /// let x = SbiRet::already_available(); + /// assert_eq!(x.map_or_else(|e| k * 2, |v| v & 0b1), 42); + /// ``` #[inline] pub fn map_or_else U, F: FnOnce(usize) -> U>( self, @@ -235,6 +368,31 @@ impl SbiRet { /// Maps a `SbiRet` to `Result` by applying a function to a /// contained error as [`Error`] struct, leaving success value untouched. + /// + /// This function can be used to pass through a successful result while handling + /// an error. + /// + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// # use sbi_spec::binary::{SbiRet, Error}; + /// fn stringify(x: Error) -> String { + /// if x == Error::AlreadyStarted { + /// format!("error: already started!") + /// } else { + /// format!("error: other error!") + /// } + /// } + /// + /// let x = SbiRet::success(2); + /// assert_eq!(x.map_err(stringify), Ok(2)); + /// + /// let x = SbiRet::already_started(); + /// assert_eq!(x.map_err(stringify), Err("error: already started!".to_string())); + /// ``` #[inline] pub fn map_err F>(self, op: O) -> Result { self.into_result().map_err(op) @@ -246,6 +404,16 @@ impl SbiRet { /// /// Panics if self is an SBI error with a panic message including the /// passed message, and the content of the SBI state. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ```should_panic + /// # use sbi_spec::binary::SbiRet; + /// let x = SbiRet::already_stopped(); + /// x.expect("Testing expect"); // panics with `Testing expect` + /// ``` #[inline] pub fn expect(self, msg: &str) -> usize { self.into_result().expect(msg) @@ -257,6 +425,22 @@ impl SbiRet { /// /// Panics if self is an SBI error, with a panic message provided by the /// SBI error converted into [`Error`] struct. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// # use sbi_spec::binary::SbiRet; + /// let x = SbiRet::success(2); + /// assert_eq!(x.unwrap(), 2); + /// ``` + /// + /// ```should_panic + /// # use sbi_spec::binary::SbiRet; + /// let x = SbiRet::failed(); + /// x.unwrap(); // panics + /// ``` #[inline] pub fn unwrap(self) -> usize { self.into_result().unwrap() @@ -268,6 +452,16 @@ impl SbiRet { /// /// Panics if the self is SBI success value, with a panic message including the /// passed message, and the content of the success value. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ```should_panic + /// # use sbi_spec::binary::SbiRet; + /// let x = SbiRet::success(10); + /// x.expect_err("Testing expect_err"); // panics with `Testing expect_err` + /// ``` #[inline] pub fn expect_err(self, msg: &str) -> Error { self.into_result().expect_err(msg) @@ -279,6 +473,20 @@ impl SbiRet { /// /// Panics if the self is SBI success value, with a custom panic message provided /// by the success value. + /// + /// # Examples + /// + /// ```should_panic + /// # use sbi_spec::binary::SbiRet; + /// let x = SbiRet::success(2); + /// x.unwrap_err(); // panics with `2` + /// ``` + /// + /// ``` + /// # use sbi_spec::binary::{SbiRet, Error}; + /// let x = SbiRet::not_supported(); + /// assert_eq!(x.unwrap_err(), Error::NotSupported); + /// ``` #[inline] pub fn unwrap_err(self) -> Error { self.into_result().unwrap_err() @@ -286,7 +494,37 @@ impl SbiRet { /// Returns `res` if self is success value, otherwise otherwise returns the contained error /// of `self` as [`Error`] struct. + /// + /// Arguments passed to `and` are eagerly evaluated; if you are passing the + /// result of a function call, it is recommended to use [`and_then`], which is + /// lazily evaluated. + /// + /// [`and_then`]: SbiRet::and_then + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// # use sbi_spec::binary::{SbiRet, Error}; + /// let x = SbiRet::success(2); + /// let y = SbiRet::invalid_param().into_result(); + /// assert_eq!(x.and(y), Err(Error::InvalidParam)); + /// + /// let x = SbiRet::denied(); + /// let y = SbiRet::success(3).into_result(); + /// assert_eq!(x.and(y), Err(Error::Denied)); + /// + /// let x = SbiRet::invalid_address(); + /// let y = SbiRet::already_available().into_result(); + /// assert_eq!(x.and(y), Err(Error::InvalidAddress)); + /// + /// let x = SbiRet::success(4); + /// let y = SbiRet::success(5).into_result(); + /// assert_eq!(x.and(y), Ok(5)); + /// ``` // fixme: should be pub const fn once this function in Result is stablized in constant + // fixme: should parameter be `res: SbiRet`? #[inline] pub fn and(self, res: Result) -> Result { self.into_result().and(res) @@ -294,25 +532,105 @@ impl SbiRet { /// Calls `op` if self is success value, otherwise returns the contained error /// as [`Error`] struct. + /// + /// This function can be used for control flow based on `SbiRet` values. + /// + /// # Examples + /// + /// ``` + /// # use sbi_spec::binary::{SbiRet, Error}; + /// fn sq_then_to_string(x: usize) -> Result { + /// x.checked_mul(x).map(|sq| sq.to_string()).ok_or(Error::Failed) + /// } + /// + /// assert_eq!(SbiRet::success(2).and_then(sq_then_to_string), Ok(4.to_string())); + /// assert_eq!(SbiRet::success(1_000_000_000_000).and_then(sq_then_to_string), Err(Error::Failed)); + /// assert_eq!(SbiRet::invalid_param().and_then(sq_then_to_string), Err(Error::InvalidParam)); + /// ``` #[inline] pub fn and_then Result>(self, op: F) -> Result { self.into_result().and_then(op) } /// Returns `res` if self is SBI error, otherwise returns the success value of `self`. + /// + /// Arguments passed to `or` are eagerly evaluated; if you are passing the + /// result of a function call, it is recommended to use [`or_else`], which is + /// lazily evaluated. + /// + /// [`or_else`]: Result::or_else + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// # use sbi_spec::binary::{SbiRet, Error}; + /// let x = SbiRet::success(2); + /// let y = SbiRet::invalid_param().into_result(); + /// assert_eq!(x.or(y), Ok(2)); + /// + /// let x = SbiRet::denied(); + /// let y = SbiRet::success(3).into_result(); + /// assert_eq!(x.or(y), Ok(3)); + /// + /// let x = SbiRet::invalid_address(); + /// let y = SbiRet::already_available().into_result(); + /// assert_eq!(x.or(y), Err(Error::AlreadyAvailable)); + /// + /// let x = SbiRet::success(4); + /// let y = SbiRet::success(100).into_result(); + /// assert_eq!(x.or(y), Ok(4)); + /// ``` // fixme: should be pub const fn once this function in Result is stablized in constant + // fixme: should parameter be `res: SbiRet`? #[inline] pub fn or(self, res: Result) -> Result { self.into_result().or(res) } /// Calls `op` if self is SBI error, otherwise returns the success value of `self`. + /// + /// This function can be used for control flow based on result values. + /// + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// # use sbi_spec::binary::{SbiRet, Error}; + /// fn is_failed(x: Error) -> Result { Err(x == Error::Failed) } + /// + /// assert_eq!(SbiRet::success(2).or_else(is_failed), Ok(2)); + /// assert_eq!(SbiRet::failed().or_else(is_failed), Err(true)); + /// ``` #[inline] pub fn or_else Result>(self, op: O) -> Result { self.into_result().or_else(op) } /// Returns the contained success value or a provided default. + /// + /// Arguments passed to `unwrap_or` are eagerly evaluated; if you are passing + /// the result of a function call, it is recommended to use [`unwrap_or_else`], + /// which is lazily evaluated. + /// + /// [`unwrap_or_else`]: SbiRet::unwrap_or_else + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// # use sbi_spec::binary::SbiRet; + /// let default = 2; + /// let x = SbiRet::success(9); + /// assert_eq!(x.unwrap_or(default), 9); + /// + /// let x = SbiRet::invalid_param(); + /// assert_eq!(x.unwrap_or(default), default); + /// ``` // fixme: should be pub const fn once this function in Result is stablized in constant #[inline] pub fn unwrap_or(self, default: usize) -> usize { @@ -320,6 +638,18 @@ impl SbiRet { } /// Returns the contained success value or computes it from a closure. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// # use sbi_spec::binary::{SbiRet, Error}; + /// fn invalid_use_zero(x: Error) -> usize { if x == Error::InvalidParam { 0 } else { 3 } } + /// + /// assert_eq!(SbiRet::success(2).unwrap_or_else(invalid_use_zero), 2); + /// assert_eq!(SbiRet::invalid_param().unwrap_or_else(invalid_use_zero), 0); + /// ``` #[inline] pub fn unwrap_or_else usize>(self, op: F) -> usize { self.into_result().unwrap_or_else(op)