maycoon_core/
state.rs

1/// A trait to define the global state of an application.
2pub trait State: 'static {}
3
4/// An empty state. Useful for testing and examples.
5pub struct EmptyState;
6
7impl State for EmptyState {}
8
9/// A value that's either dependent on the state or independent.
10///
11/// Use [`Val::new_state`] or the `val!()` macro from the `maycoon-macros` crate to create a new state dependent value.
12///
13/// Use [`Val::new_val`] or just `YourValue.into()` to create an independent value. You can also use `val!()`, because why not?
14///
15/// **NOTE for Widget Developers:** Inside the [`Widget::update`] method, all values must be invalidated using [`Val::invalidate`].
16///
17/// [`Widget::update`]: crate::widget::Widget::update
18pub enum Val<S: State, T: 'static> {
19    /// A state dependent value.
20    ///
21    /// The inner value may need to be re-computed, when invalid.
22    State {
23        /// The factory that produces the inner value.
24        ///
25        /// This is called in order to "compute" the value `T`.
26        factory: Box<dyn Fn(&S) -> T>,
27        /// The inner value.
28        ///
29        /// If computed (valid), this is `Some(T)`. If not computed (invalid), this is `None`.
30        value: Option<T>,
31    },
32
33    /// An independent value.
34    ///
35    /// The inner value will always be the same and does not need any re-computation.
36    ///
37    /// More performant than state dependent values.
38    Val(T),
39}
40
41impl<S: State, T: 'static> Val<S, T> {
42    /// Create a new state dependent value from the given closure.
43    pub fn new_state(factory: impl Fn(&S) -> T + 'static) -> Self {
44        Self::State {
45            factory: Box::new(factory),
46            value: None,
47        }
48    }
49
50    /// Create a new state independent value.
51    pub fn new_val(value: T) -> Self {
52        Self::Val(value)
53    }
54
55    /// Get the inner value of this [`Val`].
56    ///
57    /// This will compute the value if it's state dependent and not yet computed, so it will always return something.
58    pub fn get(mut self, state: &S) -> T {
59        if self.invalid() {
60            self.compute(state);
61        }
62
63        self.value().unwrap()
64    }
65
66    /// Get a reference to the inner value of this [`Val`].
67    ///
68    /// This will compute the value if it's state dependent and not yet computed, so it will always return something.
69    pub fn get_ref(&mut self, state: &S) -> &T {
70        if self.invalid() {
71            self.compute(state);
72        }
73
74        self.value_ref().unwrap()
75    }
76
77    /// Get a mutable reference to the inner value of this [`Val`].
78    ///
79    /// This will compute the value if it's state dependent and not yet computed, so it will always return something.
80    pub fn get_mut(&mut self, state: &S) -> &mut T {
81        if self.invalid() {
82            self.compute(state);
83        }
84
85        self.value_mut().unwrap()
86    }
87
88    /// If the inner value is state dependent, compute it using the given state.
89    pub fn compute(&mut self, state: &S) {
90        match self {
91            Val::State { factory, value } => {
92                *value = Some(factory(state));
93            },
94
95            Val::Val(_) => (),
96        }
97    }
98
99    /// Returns if the inner value is state dependent and not yet computed.
100    pub fn invalid(&self) -> bool {
101        self.value_ref().is_none()
102    }
103
104    /// Invalidates the inner value, if it's state dependent.
105    ///
106    /// This makes [`Val::get`] and other methods re-compute the value (if it's state dependent).
107    pub fn invalidate(&mut self) {
108        match self {
109            Val::State { value, .. } => *value = None,
110            Val::Val(_) => (),
111        }
112    }
113
114    /// Returns the inner value or [`None`] if it's state dependent and not yet computed.
115    pub fn value(self) -> Option<T> {
116        match self {
117            Val::State { value, .. } => value,
118            Val::Val(v) => Some(v),
119        }
120    }
121
122    /// Returns a reference to the inner value or [`None`] if it's state dependent and not yet computed.
123    pub fn value_ref(&self) -> Option<&T> {
124        match self {
125            Val::State { value, .. } => value.as_ref(),
126            Val::Val(v) => Some(v),
127        }
128    }
129
130    /// Returns a mutable reference to the inner value or [`None`] if it's state dependent and not yet computed.
131    pub fn value_mut(&mut self) -> Option<&mut T> {
132        match self {
133            Val::State { value, .. } => value.as_mut(),
134            Val::Val(v) => Some(v),
135        }
136    }
137
138    /// Applies a function to the inner value, transforming it into another value.
139    ///
140    /// Similar to the [`Option::map`] method.
141    pub fn map<U, F: Fn(T) -> U + 'static>(self, f: F) -> Val<S, U> {
142        match self {
143            Val::State { factory, .. } => Val::<S, U>::State {
144                factory: Box::new(move |state| f(factory(state))),
145                value: None,
146            },
147
148            Val::Val(val) => Val::Val(f(val)),
149        }
150    }
151}
152
153impl<S: State, T> From<T> for Val<S, T> {
154    fn from(value: T) -> Self {
155        Self::new_val(value)
156    }
157}