Skip to main content

ferroui_dynamic_data/kernel/
option_extensions.rs

1//! `OptionExtensions` (`Kernel/OptionExtensions.cs`): the extensions of `Optional<T>`, which is
2//! `Option<T>` in the port, and the lookups that return one.
3//!
4//! The overloads that the standard library already has keep its form: `OrElse` is
5//! `Option::or_else`, `ValueOr(T?, T)` of a nullable value is `Option::unwrap_or`, and
6//! `IfHasValue(Optional<T>?)` of a nullable option is `Option::flatten` followed by
7//! [`if_has_value`](OptionExtensions::if_has_value).
8
9use super::OptionElse;
10use std::collections::HashMap;
11use std::hash::Hash;
12
13/// The extensions of an option (`OptionExtensions` on `Optional<T>`).
14pub trait OptionExtensions<T>: Sized {
15    /// The value converted by `converter`, or none (`Convert(Func<TSource, TDestination>)`).
16    fn convert<U>(self, converter: impl FnOnce(T) -> U) -> Option<U>;
17
18    /// The option that `converter` makes of the value, or none
19    /// (`Convert(Func<TSource, Optional<TDestination>>)`).
20    fn convert_optional<U>(self, converter: impl FnOnce(T) -> Option<U>) -> Option<U>;
21
22    /// The value converted by `converter`, or what `fallback_converter` makes when there is none
23    /// (`ConvertOr`).
24    fn convert_or<U>(self, converter: impl FnOnce(T) -> U, fallback_converter: impl FnOnce() -> U) -> U;
25
26    /// Runs `action` with the value; the result runs its `else_` action when there is none
27    /// (`IfHasValue(Action<T>)`).
28    fn if_has_value(self, action: impl FnOnce(T)) -> OptionElse;
29
30    /// The value, or what `value_selector` makes when there is none (`ValueOr(Func<T>)`).
31    fn value_or(self, value_selector: impl FnOnce() -> T) -> T;
32
33    /// The value, or the default value of `T` (`ValueOrDefault`).
34    fn value_or_default(self) -> T
35    where
36        T: Default;
37
38    /// The value, or the error that `exception_generator` makes when there is none
39    /// (`ValueOrThrow`).
40    ///
41    /// Deviation (DEVIATIONS.md, DynamicData): upstream throws the exception; the port returns it
42    /// as the `Err` of the result.
43    fn value_or_throw<E>(self, exception_generator: impl FnOnce() -> E) -> Result<T, E>;
44}
45
46impl<T> OptionExtensions<T> for Option<T> {
47    fn convert<U>(self, converter: impl FnOnce(T) -> U) -> Option<U> {
48        self.map(converter)
49    }
50
51    fn convert_optional<U>(self, converter: impl FnOnce(T) -> Option<U>) -> Option<U> {
52        self.and_then(converter)
53    }
54
55    fn convert_or<U>(self, converter: impl FnOnce(T) -> U, fallback_converter: impl FnOnce() -> U) -> U {
56        match self {
57            Some(value) => converter(value),
58            None => fallback_converter(),
59        }
60    }
61
62    fn if_has_value(self, action: impl FnOnce(T)) -> OptionElse {
63        match self {
64            Some(value) => {
65                action(value);
66                OptionElse::no_action()
67            }
68            None => OptionElse::run(),
69        }
70    }
71
72    fn value_or(self, value_selector: impl FnOnce() -> T) -> T {
73        self.unwrap_or_else(value_selector)
74    }
75
76    fn value_or_default(self) -> T
77    where
78        T: Default,
79    {
80        self.unwrap_or_default()
81    }
82
83    fn value_or_throw<E>(self, exception_generator: impl FnOnce() -> E) -> Result<T, E> {
84        self.ok_or_else(exception_generator)
85    }
86}
87
88/// The first item that `selector` accepts, or none (`FirstOrOptional`).
89pub fn first_or_optional<T>(source: impl IntoIterator<Item = T>, selector: impl Fn(&T) -> bool) -> Option<T> {
90    source.into_iter().find(|item| selector(item))
91}
92
93/// The value of `key` in `source`, or none (`Lookup(IDictionary, TKey)`).
94pub fn lookup<K: Eq + Hash, V: Clone>(source: &HashMap<K, V>, key: &K) -> Option<V> {
95    source.get(key).cloned()
96}
97
98/// Removes `key` from `source`; whether it was there (`RemoveIfContained`).
99pub fn remove_if_contained<K: Eq + Hash, V>(source: &mut HashMap<K, V>, key: &K) -> bool {
100    source.remove(key).is_some()
101}
102
103/// The values of the options that have one (`SelectValues(IEnumerable<Optional<T>>)`).
104pub fn select_values<T>(source: impl IntoIterator<Item = Option<T>>) -> Vec<T> {
105    source.into_iter().flatten().collect()
106}