Skip to content

Commit

Permalink
feat(corelib): add 'map' methods to 'Result'
Browse files Browse the repository at this point in the history
  • Loading branch information
cairolover committed Dec 26, 2024
1 parent d6f6e5d commit e48169e
Show file tree
Hide file tree
Showing 2 changed files with 170 additions and 0 deletions.
114 changes: 114 additions & 0 deletions corelib/src/result.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -540,4 +540,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, ByteArray> = Result::Ok("foo");
/// assert!(x.map_or_else(|e: ByteArray| k * 2, |v: ByteArray| v.len()) == 3);
///
/// let x : Result<ByteArray, 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, ByteArray> = Result::Ok("foo");
assert!(x.map_or_else(|_e| k * 2, |v: ByteArray| v.len()) == 3);
}

#[test]
fn test_result_err_map_or_else() {
let k = 21;
let x: Result<ByteArray, 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 e48169e

Please sign in to comment.