orx_closure/
closure_res_ref.rs

1use crate::fun::FunResRef;
2use core::fmt::Debug;
3
4/// Closure strictly separating the captured data from the function, and hence, having two components:
5///
6/// * `Capture` is any captured data,
7/// * `fn(&Capture, In) -> Result<&Out, Error>` is the transformation.
8///
9/// It represents the transformation `In -> Result<&Out, Error>`.
10///
11/// Note that, unlike trait objects of fn-traits, `Capture` auto-implements `Clone` given that captured data is cloneable.
12///
13/// **Instead of `ClosureResRef`; this closure variant is particularly useful when we capture the data by value and return an `Result`` where the Ok variant is a reference.**
14///
15/// # Example
16///
17/// ```rust
18/// use orx_closure::Capture;
19///
20/// struct Person { name: String }
21/// let people = [Person { name: "john".to_string() }, Person { name: "doe".to_string() }];
22/// // name_of_person_with_id: ClosureResRef<[Person; 2], usize, str, String>
23/// let name_of_person_with_id = Capture(people).fun_result_ref(|ppl, id: usize| {
24///     ppl.get(id)
25///         .map(|p| p.name.as_str())
26///         .ok_or_else(|| "unknown id".to_string())
27/// });
28///
29/// assert_eq!(Ok("john"), name_of_person_with_id.call(0));
30/// assert_eq!(Err("unknown id".to_string()), name_of_person_with_id.call(42));
31///
32/// // alternatively
33/// let fun = name_of_person_with_id.as_fn();
34/// assert_eq!(Ok("doe"), fun(1));
35/// ```
36#[derive(Clone)]
37pub struct ClosureResRef<Capture, In, Out: ?Sized, Error> {
38    capture: Capture,
39    fun: fn(&Capture, In) -> Result<&Out, Error>,
40}
41
42impl<Capture: Debug, In, Out: ?Sized, Error> Debug for ClosureResRef<Capture, In, Out, Error> {
43    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
44        f.debug_struct("ClosureResRef")
45            .field("capture", &self.capture)
46            .finish()
47    }
48}
49
50impl<Capture, In, Out: ?Sized, Error> ClosureResRef<Capture, In, Out, Error> {
51    pub(super) fn new(capture: Capture, fun: fn(&Capture, In) -> Result<&Out, Error>) -> Self {
52        Self { capture, fun }
53    }
54
55    /// Calls the closure with the given `input`.
56    ///
57    /// # Example
58    ///
59    /// ```rust
60    /// use orx_closure::Capture;
61    ///
62    /// struct Person { name: String }
63    /// let people = [Person { name: "john".to_string() }, Person { name: "doe".to_string() }];
64    /// // name_of_person_with_id: ClosureResRef<[Person; 2], usize, str, String>
65    /// let name_of_person_with_id = Capture(people).fun_result_ref(|ppl, id: usize| {
66    ///     ppl.get(id)
67    ///         .map(|p| p.name.as_str())
68    ///         .ok_or_else(|| "unknown id".to_string())
69    /// });
70    ///
71    /// assert_eq!(Ok("john"), name_of_person_with_id.call(0));
72    /// assert_eq!(Err("unknown id".to_string()), name_of_person_with_id.call(42));
73    /// ```
74    #[inline(always)]
75    pub fn call(&self, input: In) -> Result<&Out, Error> {
76        (self.fun)(&self.capture, input)
77    }
78
79    /// Returns a reference to the captured data.
80    #[inline(always)]
81    pub fn captured_data(&self) -> &Capture {
82        &self.capture
83    }
84
85    /// Consumes the closure and returns back the captured data.
86    ///
87    /// ```rust
88    /// use orx_closure::Capture;
89    ///
90    /// struct ExpensiveData(Vec<i32>);
91    ///
92    /// let data = ExpensiveData(vec![10, 11, 12]);
93    ///
94    /// let get_number = Capture(data).fun_result_ref(|data, i| data.0.get(i).ok_or("!!"));
95    ///
96    /// assert_eq!(Ok(&10), get_number.call(0));
97    /// assert_eq!(Ok(&12), get_number.call(2));
98    ///
99    /// let _data: ExpensiveData = get_number.into_captured_data();
100    /// ```
101    pub fn into_captured_data(self) -> Capture {
102        self.capture
103    }
104
105    /// Returns the closure as an `impl Fn(In) -> Result<&Out, String>` struct, allowing the convenience
106    ///
107    /// * to avoid the `call` method,
108    /// * or pass the closure to functions accepting a function generic over the `Fn`.
109    ///
110    /// # Example
111    ///
112    /// ```rust
113    /// use orx_closure::Capture;
114    ///
115    /// struct Person { name: String }
116    /// let people = [Person { name: "john".to_string() }, Person { name: "doe".to_string() }];
117    /// // name_of_person_with_id: ClosureResRef<[Person; 2], usize, str, String>
118    /// let name_of_person_with_id = Capture(people).fun_result_ref(|ppl, id: usize| {
119    ///     ppl.get(id)
120    ///         .map(|p| p.name.as_str())
121    ///         .ok_or_else(|| "unknown id".to_string())
122    /// });
123    ///
124    /// let fun = name_of_person_with_id.as_fn();
125    /// assert_eq!(Ok("doe"), fun(1));
126    /// ```
127    pub fn as_fn<'a>(&'a self) -> impl Fn(In) -> Result<&'a Out, Error> {
128        move |x| self.call(x)
129    }
130}
131
132impl<Capture, In, Out: ?Sized, Error> FunResRef<In, Out, Error>
133    for ClosureResRef<Capture, In, Out, Error>
134{
135    fn call(&self, input: In) -> Result<&Out, Error> {
136        ClosureResRef::call(self, input)
137    }
138}