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}