with_cell/
lib.rs

1#![no_std]
2#![doc = include_str!("../README.md")]
3#![forbid(unsafe_code)] // :)
4#![deny(missing_docs)]
5
6use core::{cell::Cell, fmt, ops};
7
8/// `Cell`-like container for making shared structures with mutable methods more convenient to use.
9///
10/// Unlike [`Cell`], this wrapper does not require [`Copy`] in the general case.
11/// Instead, it relies on [`Default`].
12///
13/// Internally, it uses [`Cell`].
14#[derive(Default)]
15pub struct WithCell<T>(Cell<T>);
16
17impl<T> WithCell<T> {
18    /// Create a new `WithCell` containing the given value.
19    pub const fn new(value: T) -> Self {
20        Self(Cell::new(value))
21    }
22}
23
24impl<T> WithCell<T>
25where
26    T: Default,
27{
28    /// Perform an operation on the contained value.
29    ///
30    /// This takes the value out of the cell and replaces it with its [`Default`] variant.
31    /// This value is then passed by reference to `f`.
32    /// When `f` returns, the value is put back in the cell,
33    /// discarding the stub variant.
34    pub fn with<F, R>(&self, f: F) -> R
35    where
36        F: FnOnce(&mut T) -> R,
37    {
38        let mut v = self.0.take();
39        let ret = (f)(&mut v);
40        self.0.set(v);
41        ret
42    }
43
44    /// Perform an operation on the contained value.
45    ///
46    /// This takes the value out of the cell and replaces it with its [`Default`] variant.
47    /// This value is then passed by reference to `f`.
48    /// When `f` returns, the value is put back in the cell,
49    /// discarding the stub variant.
50    ///
51    /// This method is almost identical to [`with`](Self::with),
52    /// except it returns a reference to `self`.
53    pub fn inspect<F>(&self, f: F) -> &Self
54    where
55        F: FnOnce(&mut T),
56    {
57        self.with(f);
58        self
59    }
60
61    /// Perform an operation on the contained value.
62    ///
63    /// Like [`with`](Self::with), it replaces the value with its [`Default`] variant.
64    /// This value is then passed by value to `f`.
65    /// The value returned from `f` is put in the cell,
66    /// discarding the stub variant.
67    ///
68    /// This function can be chained:
69    /// ```
70    /// let abcd = with_cell::WithCell::new(String::new());
71    ///
72    /// abcd.map(|x| x + "a")
73    ///     .map(|x| x + "b")
74    ///     .map(|x| x + "cd")
75    ///     .map(|x| dbg!(x))
76    ///     .with(|x| x.clear());
77    /// ```
78    pub fn map<F>(&self, f: F) -> &Self
79    where
80        F: FnOnce(T) -> T,
81    {
82        self.0.set((f)(self.0.take()));
83        self
84    }
85}
86
87impl<T> ops::Deref for WithCell<T> {
88    type Target = Cell<T>;
89
90    fn deref(&self) -> &Self::Target {
91        &self.0
92    }
93}
94
95impl<T> ops::DerefMut for WithCell<T> {
96    fn deref_mut(&mut self) -> &mut Self::Target {
97        &mut self.0
98    }
99}
100
101impl<T> fmt::Debug for WithCell<T>
102where
103    T: Default + fmt::Debug,
104{
105    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
106        self.with(|x| x.fmt(f))
107    }
108}
109
110impl<T> Clone for WithCell<T>
111where
112    T: Default + Clone,
113{
114    fn clone(&self) -> Self {
115        self.with(|x| x.clone()).into()
116    }
117}
118
119impl<T> From<T> for WithCell<T> {
120    fn from(x: T) -> Self {
121        Self(x.into())
122    }
123}