Skip to content

Commit

Permalink
feat: add context to Result (#1595)
Browse files Browse the repository at this point in the history
# Summary
Adds `context` and `with_context` to `Result` - based on `anyhows`'s
`Context` trait.
  • Loading branch information
hal3e authored Feb 10, 2025
1 parent f2a7f27 commit 452253d
Showing 1 changed file with 128 additions and 0 deletions.
128 changes: 128 additions & 0 deletions packages/fuels-core/src/types/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,31 @@ pub mod transaction {
#[error(": {0}")]
Other(String),
}

impl Reason {
pub(crate) fn context(self, context: impl std::fmt::Display) -> Self {
match self {
Reason::Builder(msg) => Reason::Builder(format!("{context}: {msg}")),
Reason::Validation(msg) => Reason::Validation(format!("{context}: {msg}")),
Reason::SqueezedOut(msg) => Reason::SqueezedOut(format!("{context}: {msg}")),
Reason::Reverted {
reason,
revert_id,
receipts,
} => Reason::Reverted {
reason: format!("{context}: {reason}"),
revert_id,
receipts,
},
Reason::Other(msg) => Reason::Other(format!("{context}: {msg}")),
}
}
}
}

use crate::sealed::Sealed;
use std::fmt::Display;

#[derive(thiserror::Error, Debug, Clone)]
pub enum Error {
#[error("io: {0}")]
Expand All @@ -38,8 +61,64 @@ impl From<std::io::Error> for Error {
}
}

impl Error {
pub(crate) fn context(self, context: impl Display) -> Self {
match self {
Error::IO(msg) => Error::IO(format!("{context}: {msg}")),
Error::Codec(msg) => Error::Codec(format!("{context}: {msg}")),
Error::Transaction(reason) => Error::Transaction(reason.context(context)),
Error::Provider(msg) => Error::Provider(format!("{context}: {msg}")),
Error::Other(msg) => Error::Other(format!("{context}: {msg}")),
}
}
}

pub type Result<T> = std::result::Result<T, Error>;

/// Provides `context` and `with_context` to `Result`.
///
/// # Examples
/// ```
/// use fuels_core::types:: errors::{Context, Error, Result};
///
/// let res_with_context: Result<()> =
/// Err(Error::Other("some error".to_owned())).context("some context");
///
/// let res_with_context: Result<()> =
/// Err(Error::Other("some error".to_owned())).with_context(|| "some context");
/// ```
pub trait Context<T>: Sealed {
fn context<C>(self, context: C) -> Result<T>
where
C: Display + Send + Sync + 'static;

fn with_context<C, F>(self, f: F) -> Result<T>
where
C: Display + Send + Sync + 'static,
F: FnOnce() -> C;
}

impl<T> Sealed for Result<T> {}

impl<T> Context<T> for Result<T> {
/// Wrap the error value with additional context
fn context<C>(self, context: C) -> Result<T>
where
C: Display + Send + Sync + 'static,
{
self.map_err(|e| e.context(context))
}

/// Wrap the error value with additional context that is evaluated lazily
fn with_context<C, F>(self, context: F) -> Result<T>
where
C: Display + Send + Sync + 'static,
F: FnOnce() -> C,
{
self.context(context())
}
}

/// This macro can only be used for `Error` variants that have a `String` field.
/// Those are: `IO`, `Codec`, `Provider`, `Other`.
#[macro_export]
Expand Down Expand Up @@ -91,3 +170,52 @@ impl_error_from!(Other, hex::FromHexError);
impl_error_from!(Other, std::array::TryFromSliceError);
impl_error_from!(Other, std::str::Utf8Error);
impl_error_from!(Other, fuel_abi_types::error::Error);

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn result_context() {
{
let res_with_context: Result<()> =
Err(error!(Provider, "some error")).context("some context");

assert_eq!(
res_with_context.unwrap_err().to_string(),
"provider: some context: some error",
);
}
{
let res_with_context: Result<()> =
Err(error_transaction!(Builder, "some error")).context("some context");

assert_eq!(
res_with_context.unwrap_err().to_string(),
"transaction builder: some context: some error"
);
}
}

#[test]
fn result_with_context() {
{
let res_with_context: Result<()> =
Err(error!(Other, "some error")).with_context(|| "some context");

assert_eq!(
res_with_context.unwrap_err().to_string(),
"some context: some error",
);
}
{
let res_with_context: Result<()> =
Err(error_transaction!(Validation, "some error")).with_context(|| "some context");

assert_eq!(
res_with_context.unwrap_err().to_string(),
"transaction validation: some context: some error"
);
}
}
}

0 comments on commit 452253d

Please sign in to comment.