Skip to content

Commit

Permalink
feat(corelib): add map methods to Result (#6932)
Browse files Browse the repository at this point in the history
  • Loading branch information
cairolover authored Jan 4, 2025
1 parent cc7f05b commit d4603c2
Show file tree
Hide file tree
Showing 2 changed files with 201 additions and 0 deletions.
145 changes: 145 additions & 0 deletions corelib/src/result.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -81,11 +81,42 @@
//!
//! ## Transforming contained values
//!
//! These methods transform [`Result`] to [`Option`]:
//!
//! * [`ok`] transforms [`Result<T, E>`] into [`Option<T>`], mapping
//! [`Ok(v)`] to [`Some(v)`] and [`Err(e)`] to [`None`]
//! * [`err`] transforms [`Result<T, E>`] into [`Option<E>`], mapping
//! [`Ok(v)`] to [`None`] and [`Err(e)`] to [`Some(e)`]
//!
//! This method transforms the contained value of the [`Ok`] variant:
//!
//! * [`map`] transforms [`Result<T, E>`] into [`Result<U, E>`] by applying
//! the provided function to the contained value of [`Ok`] and leaving
//! [`Err`] values unchanged
//!
//! [`map`]: ResultTrait::map
//!
//! This method transforms the contained value of the [`Err`] variant:
//!
//! * [`map_err`] transforms [`Result<T, E>`] into [`Result<T, F>`] by
//! applying the provided function to the contained value of [`Err`] and
//! leaving [`Ok`] values unchanged
//!
//! [`map_err`]: ResultTrait::map_err
//!
//! These methods transform a [`Result<T, E>`] into a value of a possibly
//! different type `U`:
//!
//! * [`map_or`] applies the provided function to the contained value of
//! [`Ok`], or returns the provided default value if the [`Result`] is
//! [`Err`]
//! * [`map_or_else`] applies the provided function to the contained value
//! of [`Ok`], or applies the provided default fallback function to the
//! contained value of [`Err`]
//!
//! [`map_or`]: ResultTrait::map_or
//! [`map_or_else`]: ResultTrait::map_or_else
//!
//! ## Boolean operators
//!
//! These methods treat the [`Result`] as a boolean value, where [`Ok`]
Expand Down Expand Up @@ -540,4 +571,118 @@ pub impl ResultTraitImpl<T, E> of ResultTrait<T, E> {
Result::Err(x) => Option::Some(x),
}
}

/// Maps a `Result<T, E>` to `Result<U, E>` by applying a function to a
/// contained [`Ok`] value, leaving an [`Err`] value untouched.
///
/// This function can be used to compose the results of two functions.
///
/// # Examples
///
/// Print the square of the number contained in the `Result`, otherwise print the error.
///
/// ```
/// let inputs: Array<Result<u32, ByteArray>> = array![
/// Result::Ok(1), Result::Err("error"), Result::Ok(3), Result::Ok(4),
/// ];
/// for i in inputs {
/// match i.map(|i| i * 2) {
/// Result::Ok(x) => println!("{x}"),
/// Result::Err(e) => println!("{e}"),
/// }
/// }
/// ```
#[inline]
fn map<U, F, +Drop<F>, +core::ops::FnOnce<F, (T,)>[Output: U]>(
self: Result<T, E>, f: F,
) -> Result<U, E> {
match self {
Result::Ok(x) => Result::Ok(f(x)),
Result::Err(e) => Result::Err(e),
}
}

/// Returns the provided default (if [`Err`]), or
/// applies a function to the contained value (if [`Ok`]).
///
/// # Examples
///
/// ```
/// let x: Result<_, ByteArray> = Result::Ok("foo");
/// assert!(x.map_or(42, |v: ByteArray| v.len()) == 3);
///
/// let x: Result<_, ByteArray> = Result::Err("bar");
/// assert!(x.map_or(42, |v: ByteArray| v.len()) == 42);
/// ```
#[inline]
fn map_or<U, F, +Destruct<E>, +Destruct<U>, +Drop<F>, +core::ops::FnOnce<F, (T,)>[Output: U]>(
self: Result<T, E>, default: U, f: F,
) -> U {
match self {
Result::Ok(x) => f(x),
Result::Err(_) => default,
}
}

/// Maps a `Result<T, E>` to `U` by applying fallback function `default` to
/// a contained [`Err`] value, or function `f` to a contained [`Ok`] value.
///
/// This function can be used to unpack a successful result
/// while handling an error.
///
///
/// # Examples
///
/// ```
/// let k = 21;
///
/// let x: Result<ByteArray, _> = Result::Ok("foo");
/// assert!(x.map_or_else(|_e: ByteArray| k * 2, |v: ByteArray| v.len()) == 3);
///
/// let x: Result<_, ByteArray> = Result::Err("bar");
/// assert!(x.map_or_else(|_e: ByteArray| k * 2, |v: ByteArray| v.len()) == 42);
/// ```
#[inline]
fn map_or_else<
U,
D,
F,
+Drop<D>,
+Drop<F>,
+core::ops::FnOnce<D, (E,)>[Output: U],
+core::ops::FnOnce<F, (T,)>[Output: U],
>(
self: Result<T, E>, default: D, f: F,
) -> U {
match self {
Result::Ok(t) => f(t),
Result::Err(e) => default(e),
}
}

/// Maps a `Result<T, E>` to `Result<T, F>` by applying a function to a
/// contained [`Err`] value, leaving an [`Ok`] value untouched.
///
/// This function can be used to pass through a successful result while handling
/// an error.
///
///
/// # Examples
///
/// ```
/// let stringify = |x: u32| -> ByteArray { format!("error code: {x}") };
/// let x: Result<u32, u32> = Result::Ok(2);
/// assert!(x.map_err(stringify) == Result::<u32, ByteArray>::Ok(2));
///
/// let x: Result<u32, u32> = Result::Err(13);
/// assert!(x.map_err(stringify) == Result::Err("error code: 13"));
/// ```
fn map_err<F, O, +Drop<O>, +core::ops::FnOnce<O, (E,)>[Output: F]>(
self: Result<T, E>, op: O,
) -> Result<T, F> {
match self {
Result::Ok(x) => Result::Ok(x),
Result::Err(e) => Result::Err(op(e)),
}
}
}
56 changes: 56 additions & 0 deletions corelib/src/test/result_test.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -256,3 +256,59 @@ fn test_result_ok_err_should_return_none() {
let x: Result<u32, ByteArray> = Result::Ok(2);
assert!(x.err().is_none());
}

#[test]
fn test_result_ok_map() {
let x: Result<u32, ByteArray> = Result::Ok(1);
assert!(x.map(|i| i * 2) == Result::Ok(2));
}

#[test]
fn test_result_err_map() {
let x: Result<u32, ByteArray> = Result::Err("error");
assert!(x.map(|i| i * 2) == Result::Err("error"));
}

#[test]
fn test_result_ok_map_or() {
let x: Result<ByteArray, ByteArray> = Result::Ok("foo");
assert!(x.map_or(42, |v: ByteArray| v.len()) == 3);
}

#[test]
fn test_result_err_map_or() {
let x: Result<ByteArray, ByteArray> = Result::Err("bar");
assert!(x.map_or(42, |v: ByteArray| v.len()) == 42);
}

#[test]
fn test_result_ok_map_or_else() {
let k = 21;
let x: Result<ByteArray, _> = Result::Ok("foo");
assert!(x.map_or_else(|_e: ByteArray| k * 2, |v: ByteArray| v.len()) == 3);
}

#[test]
fn test_result_err_map_or_else() {
let k = 21;
let x: Result<_, ByteArray> = Result::Err("bar");
assert!(x.map_or_else(|_e| k * 2, |v: ByteArray| v.len()) == 42);
}

#[test]
fn test_result_ok_map_err() {
let stringify = |x: u32| -> ByteArray {
format!("error code: {}", x)
};
let x: Result<u32, u32> = Result::Ok(2);
assert!(x.map_err(stringify) == Result::<u32, ByteArray>::Ok(2));
}

#[test]
fn test_result_err_map_err() {
let stringify = |x: u32| -> ByteArray {
format!("error code: {}", x)
};
let x: Result<u32, u32> = Result::Err(13);
assert!(x.map_err(stringify) == Result::Err("error code: 13"));
}

0 comments on commit d4603c2

Please sign in to comment.