devela/sys/mem/cell/
option.rs

1// devela::sys::mem::cell::option
2//
3//! Defines [`ExtCellOption`].
4//
5
6use crate::Cell;
7
8/// Marker trait to prevent downstream implementations of the [`ExtCell`] trait.
9trait Sealed {}
10impl<T> Sealed for Cell<Option<T>> {}
11
12#[doc = crate::TAG_NAMESPACE!()]
13/// Extension trait providing additional methods for `Cell<Option>`.
14#[cfg_attr(feature = "nightly_doc", doc(notable_trait))]
15#[expect(private_bounds, reason = "Sealed")]
16pub trait ExtCellOption<T>: Sealed {
17    /// Modifies the value inside the `Cell<Option<T>>` by applying the provided closure
18    /// to a mutable reference of the current value if present.
19    ///
20    /// This method extracts the value, applies the function, and stores the result back.
21    ///
22    /// # Example
23    /// ```
24    /// use devela::{Cell, ExtCellOption};
25    ///
26    /// let cell = Cell::new(Some(10));
27    /// cell.modify(|x| x + 5);
28    /// assert_eq![cell.get(), Some(15)];
29    /// ```
30    // WAIT: [cell_update](https://github.com/rust-lang/rust/issues/50186)
31    fn modify<F: FnOnce(T) -> T>(&self, func: F);
32
33    /// Modifies the value inside the `Cell<Option<T>>` by applying the provided function
34    /// and returns the old contained value.
35    ///
36    /// # Example
37    /// ```
38    /// use devela::{Cell, ExtCellOption};
39    ///
40    /// let cell = Cell::new(Some(10));
41    /// let old = cell.modify_ret(|x| x + 5);
42    /// assert_eq![old, Some(10)];
43    /// assert_eq![cell.get(), Some(15)];
44    /// ```
45    fn modify_ret<F: FnOnce(T) -> T>(&self, func: F) -> Option<T>
46    where
47        T: Clone;
48
49    /// Modifies the value inside the `Cell<Option<T>>` by applying the provided closure
50    /// to a mutable reference of the current value if present, and returns a result.
51    ///
52    /// This method allows in-place modification via a mutable reference, returning any value.
53    ///
54    /// # Example
55    /// ```
56    /// use devela::{Cell, ExtCellOption};
57    ///
58    /// let cell = Cell::new(Some(10));
59    /// let result = cell.modify_mut(|x| {
60    ///     let old = *x;
61    ///     *x *= 2;
62    ///     old
63    /// });
64    ///
65    /// assert_eq![result, Some(10)];
66    /// assert_eq![cell.get(), Some(20)];
67    /// ```
68    fn modify_mut<R, F: FnOnce(&mut T) -> R>(&self, func: F) -> Option<R>;
69}
70
71impl<T> ExtCellOption<T> for Cell<Option<T>> {
72    fn modify<F: FnOnce(T) -> T>(&self, func: F) {
73        let mut value = self.take();
74        // If the value exists, apply the function and store it back
75        if let Some(v) = value.take() {
76            self.set(Some(func(v)));
77        }
78    }
79
80    fn modify_ret<F: FnOnce(T) -> T>(&self, func: F) -> Option<T>
81    where
82        T: Clone,
83    {
84        self.replace(None).inspect(|v| {
85            let new_value = func(v.clone());
86            self.set(Some(new_value));
87        })
88    }
89
90    fn modify_mut<R, F: FnOnce(&mut T) -> R>(&self, func: F) -> Option<R> {
91        if let Some(mut v) = self.take() {
92            // Apply the function to a mutable reference and capture the return value
93            let result = func(&mut v);
94            // Store the possibly modified value back in the Cell
95            self.set(Some(v));
96            Some(result)
97        } else {
98            None
99        }
100    }
101}
102
103#[cfg(test)]
104mod tests {
105    use super::*;
106
107    #[test]
108    fn test_modify() {
109        let cell = Cell::new(Some(10));
110        cell.modify(|x| x + 5);
111        assert_eq!(cell.get(), Some(15));
112    }
113
114    #[test]
115    fn test_modify_with_none() {
116        let cell: Cell<Option<i32>> = Cell::new(None);
117        cell.modify(|x| x + 5);
118        assert_eq!(cell.get(), None);
119    }
120
121    #[test]
122    fn test_modify_ret() {
123        let cell = Cell::new(Some(10));
124
125        let previous_value = cell.modify_ret(|x| x * 2);
126
127        assert_eq!(previous_value, Some(10));
128        assert_eq!(cell.get(), Some(20));
129    }
130
131    #[test]
132    fn test_modify_ret_with_none() {
133        let cell: Cell<Option<i32>> = Cell::new(None);
134
135        let previous_value = cell.modify_ret(|x| x * 2);
136
137        assert_eq!(previous_value, None);
138        assert_eq!(cell.get(), None);
139    }
140
141    #[test]
142    fn test_modify_mut_previous() {
143        let cell = Cell::new(Some(10));
144
145        let result = cell.modify_mut(|x| {
146            let prev = *x;
147            *x *= 2;
148            prev
149        });
150
151        assert_eq!(result, Some(10)); // previous value
152        assert_eq!(cell.get(), Some(20));
153    }
154    #[test]
155    fn test_modify_mut() {
156        let cell = Cell::new(Some(10));
157
158        let result = cell.modify_mut(|x| {
159            *x *= 2;
160            *x + 5
161        });
162
163        assert_eq!(result, Some(25));
164        assert_eq!(cell.get(), Some(20));
165    }
166
167    #[test]
168    fn test_modify_mut_with_none() {
169        let cell: Cell<Option<i32>> = Cell::new(None);
170
171        let result = cell.modify_mut(|x| {
172            *x *= 2;
173            *x + 5
174        });
175
176        assert_eq!(result, None);
177        assert_eq!(cell.get(), None);
178    }
179
180    #[test]
181    fn test_modify_mut_side_effect() {
182        let cell = Cell::new(Some(10));
183
184        let result = cell.modify_mut(|x| {
185            *x += 3;
186            "Done"
187        });
188
189        assert_eq!(result, Some("Done"));
190        assert_eq!(cell.get(), Some(13));
191    }
192}