pub mod option {
pub fn some<A>(a: A) -> Option<A> {
Some(a)
}
pub fn none<A>() -> Option<A> {
None
}
pub fn from_result<A, E>(r: Result<A, E>) -> Option<A> {
r.ok()
}
pub fn to_result<A, E>(o: Option<A>, error: impl FnOnce() -> E) -> Result<A, E> {
o.ok_or_else(error)
}
pub fn map<A, B>(o: Option<A>, f: impl FnOnce(A) -> B) -> Option<B> {
o.map(f)
}
pub fn flat_map<A, B>(o: Option<A>, f: impl FnOnce(A) -> Option<B>) -> Option<B> {
o.and_then(f)
}
pub fn get_or_else<A>(o: Option<A>, default: impl FnOnce() -> A) -> A {
o.unwrap_or_else(default)
}
pub fn or_else<A>(o: Option<A>, alt: impl FnOnce() -> Option<A>) -> Option<A> {
o.or_else(alt)
}
pub fn filter<A>(o: Option<A>, p: impl FnOnce(&A) -> bool) -> Option<A> {
o.filter(p)
}
pub fn zip<A, B>(a: Option<A>, b: Option<B>) -> Option<(A, B)> {
a.zip(b)
}
pub fn zip_with<A, B, C>(a: Option<A>, b: Option<B>, f: impl FnOnce(A, B) -> C) -> Option<C> {
a.zip(b).map(|(a, b)| f(a, b))
}
pub fn tap<A>(o: Option<A>, f: impl FnOnce(&A)) -> Option<A> {
if let Some(ref v) = o {
f(v);
}
o
}
pub fn lift_predicate<A>(a: A, p: impl Fn(&A) -> bool) -> Option<A> {
if p(&a) { Some(a) } else { None }
}
#[inline]
pub fn from_predicate<A>(a: A, p: impl Fn(&A) -> bool) -> Option<A> {
lift_predicate(a, p)
}
pub fn is_some<A>(o: &Option<A>) -> bool {
o.is_some()
}
pub fn is_none<A>(o: &Option<A>) -> bool {
o.is_none()
}
pub fn flatten<A>(o: Option<Option<A>>) -> Option<A> {
o.flatten()
}
}
#[cfg(test)]
mod tests {
use super::option;
use rstest::rstest;
mod constructors {
use super::*;
#[test]
fn some_wraps_value() {
assert_eq!(option::some(42_i32), Some(42));
}
#[test]
fn none_returns_no_value() {
assert_eq!(option::none::<i32>(), None);
}
}
mod conversions {
use super::*;
#[test]
fn from_result_ok_returns_some() {
assert_eq!(option::from_result(Ok::<i32, &str>(5)), Some(5));
}
#[test]
fn from_result_err_returns_none() {
assert_eq!(option::from_result(Err::<i32, &str>("fail")), None);
}
#[test]
fn to_result_some_returns_ok() {
assert_eq!(option::to_result(Some(1_i32), || "missing"), Ok(1));
}
#[test]
fn to_result_none_returns_err() {
assert_eq!(option::to_result(None::<i32>, || "missing"), Err("missing"));
}
}
mod map {
use super::*;
#[test]
fn some_maps_value() {
assert_eq!(option::map(Some(3_i32), |n| n * 2), Some(6));
}
#[test]
fn none_stays_none() {
assert_eq!(option::map(None::<i32>, |n| n * 2), None);
}
}
mod flat_map {
use super::*;
#[test]
fn some_to_some() {
assert_eq!(option::flat_map(Some(4_i32), |n| Some(n + 1)), Some(5));
}
#[test]
fn some_to_none() {
assert_eq!(option::flat_map(Some(0_i32), |_| None::<i32>), None);
}
#[test]
fn none_stays_none() {
assert_eq!(option::flat_map(None::<i32>, Some), None);
}
}
mod get_or_else {
use super::*;
#[test]
fn some_returns_inner() {
assert_eq!(option::get_or_else(Some(7_i32), || 99), 7);
}
#[test]
fn none_returns_default() {
assert_eq!(option::get_or_else(None::<i32>, || 99), 99);
}
}
mod or_else {
use super::*;
#[test]
fn some_ignores_alternative() {
assert_eq!(option::or_else(Some(1_i32), || Some(2)), Some(1));
}
#[test]
fn none_uses_alternative_when_some() {
assert_eq!(option::or_else(None::<i32>, || Some(2)), Some(2));
}
#[test]
fn none_uses_alternative_when_also_none() {
assert_eq!(option::or_else(None::<i32>, || None), None);
}
}
mod filter {
use super::*;
#[test]
fn some_passing_predicate_stays_some() {
assert_eq!(option::filter(Some(4_i32), |n| *n % 2 == 0), Some(4));
}
#[test]
fn some_failing_predicate_becomes_none() {
assert_eq!(option::filter(Some(3_i32), |n| *n % 2 == 0), None);
}
#[test]
fn none_stays_none() {
assert_eq!(option::filter(None::<i32>, |_| true), None);
}
}
mod zip {
use super::*;
#[rstest]
#[case::both_some(Some(1_i32), Some("a"), Some((1_i32, "a")))]
#[case::first_none(None, Some("a"), None)]
#[case::second_none(Some(1_i32), None, None)]
#[case::both_none(None, None, None)]
fn zip_cases(
#[case] a: Option<i32>,
#[case] b: Option<&str>,
#[case] expected: Option<(i32, &str)>,
) {
assert_eq!(option::zip(a, b), expected);
}
#[test]
fn zip_with_some_some_applies_function() {
assert_eq!(
option::zip_with(Some(3_i32), Some(4_i32), |a, b| a + b),
Some(7)
);
}
#[test]
fn zip_with_some_none_returns_none() {
assert_eq!(
option::zip_with(Some(1_i32), None::<i32>, |a, b| a + b),
None
);
}
}
mod tap {
use super::*;
#[test]
fn tap_on_some_calls_f_and_returns_some() {
let mut called_with = None;
let result = option::tap(Some(42_i32), |v| called_with = Some(*v));
assert_eq!(result, Some(42));
assert_eq!(called_with, Some(42));
}
#[test]
fn tap_on_none_does_not_call_f() {
let mut called = false;
let result = option::tap(None::<i32>, |_| called = true);
assert_eq!(result, None);
assert!(!called);
}
}
mod lift_predicate {
use super::*;
#[test]
fn passing_predicate_returns_some() {
assert_eq!(option::lift_predicate(4_i32, |n| *n > 0), Some(4));
}
#[test]
fn failing_predicate_returns_none() {
assert_eq!(option::lift_predicate(-1_i32, |n| *n > 0), None);
}
#[rstest]
#[case::positive(5_i32, true)]
#[case::zero(0_i32, false)]
#[case::negative(-1_i32, false)]
fn parametrised(#[case] v: i32, #[case] should_be_some: bool) {
let result = option::lift_predicate(v, |n| *n > 0);
assert_eq!(result.is_some(), should_be_some);
}
}
mod inspection {
use super::*;
#[test]
fn is_some_true_for_some() {
assert!(option::is_some(&Some(1_i32)));
}
#[test]
fn is_some_false_for_none() {
assert!(!option::is_some(&None::<i32>));
}
#[test]
fn is_none_true_for_none() {
assert!(option::is_none(&None::<i32>));
}
#[test]
fn is_none_false_for_some() {
assert!(!option::is_none(&Some(1_i32)));
}
}
mod flatten {
use super::*;
#[test]
fn some_some_flattens_to_some() {
assert_eq!(option::flatten(Some(Some(7_i32))), Some(7));
}
#[test]
fn some_none_flattens_to_none() {
assert_eq!(option::flatten(Some(None::<i32>)), None);
}
#[test]
fn outer_none_flattens_to_none() {
assert_eq!(option::flatten(None::<Option<i32>>), None);
}
}
}