atomic_hooks/
reactive_state_access.rs

1use crate::store::Reaction;
2use crate::reactive_state_functions::*;
3use std::marker::PhantomData;
4use crate::observable::Observable;
5use crate::store::StorageKey;
6// use seed::prelude::*;
7// marker types
8pub enum AllowUndo{}
9pub enum NoUndo{}
10pub  enum IsAnAtomState{}
11pub  enum IsAReactionState{}
12
13///  Accessor struct that provides access to getting and setting the
14///  state of the stored type
15///
16// #[derive(Copy)]
17pub struct ReactiveStateAccess<T,U,A> {
18    pub id: StorageKey,
19
20    pub _phantom_data_stored_type: PhantomData<T>,
21    pub _phantom_data_undo : PhantomData<U>,
22    pub _phantom_data_accessor_type : PhantomData<A>,
23}
24
25impl<T,U,A> std::fmt::Debug for ReactiveStateAccess<T,U,A> {
26    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
27        write!(f, "({:#?})", self.id)
28    }
29}
30// 
31impl<T,U,A> Clone for ReactiveStateAccess<T,U,A> {
32    fn clone(&self) -> ReactiveStateAccess<T,U,A> {
33        ReactiveStateAccess::<T,U,A> {
34            id: self.id,
35            
36            _phantom_data_stored_type: PhantomData::<T>,
37            _phantom_data_undo : PhantomData::<U>,
38            _phantom_data_accessor_type : PhantomData::<A>,
39        }
40    }
41}
42
43
44impl<T,U,A> Copy for ReactiveStateAccess<T,U,A> {}
45
46impl<T,U,A> ReactiveStateAccess<T,U,A>
47where
48    T: 'static,
49{
50    pub fn new(id: StorageKey) -> ReactiveStateAccess<T,U,A> {
51        ReactiveStateAccess {
52            id,
53            
54            _phantom_data_stored_type: PhantomData,
55            _phantom_data_undo: PhantomData,
56            _phantom_data_accessor_type: PhantomData,
57        }
58    }
59
60    // stores a value of type T in a backing Store
61    pub fn insert_set(self, value: T) where Self :OverloadedUpdateStateAccess<T>{
62        self.overloaded_inert_set(value);
63    }
64
65
66    // stores a value of type T in a backing Store
67    pub fn set(self, value: T) where Self :OverloadedUpdateStateAccess<T>{
68        self.overloaded_set(value);
69    }
70
71
72
73    pub fn remove(self) -> Option<T> {
74        remove_reactive_state_with_id(&self.id)
75    }
76
77    pub fn delete(self) {
78        self.remove();
79    }
80
81
82    pub fn undo(&self) where Self: OverloadedUpdateStateAccess<T> {
83        self.overloaded_undo();
84    }
85
86    /// updates the stored state in place
87    /// using the provided function
88    pub fn update<F: FnOnce(&mut T) -> ()>(&self, func: F) where Self :OverloadedUpdateStateAccess<T>{
89        self.overloaded_update( func); 
90    }
91
92    pub fn reset_to_default(&self) where Self :OverloadedUpdateStateAccess<T>{
93        self.overloaded_reset_to_default(); 
94    }
95
96
97    pub fn state_exists(self) -> bool {
98        reactive_state_exists_for_id::<T>(&self.id)
99    }
100
101    pub fn get_with<F: FnOnce(&T) -> R, R>(&self, func: F) -> R {
102        read_reactive_state_with_id(&self.id, func)
103    }
104
105
106
107    pub fn on_update<F: FnOnce() -> R,R>(&self, func:F) -> Option<R> {
108        let mut recalc = false ;
109        self.observe_with(|_| recalc = true);
110        if recalc {
111            Some(func())
112        } else {
113            None
114        }
115    }
116
117
118}
119
120
121// If the stored type is clone, then implement clone for ReactiveStateAccess
122pub trait CloneReactiveState<T>
123where
124    T: Clone + 'static,
125{
126    fn get(&self) -> T;
127    fn soft_get(&self) -> Option<T>;
128}
129
130impl<T,U,A> CloneReactiveState<T> for ReactiveStateAccess<T,U,A>
131where
132    T: Clone + 'static,
133{
134    /// returns a clone of the stored state panics if not stored.
135    fn get(&self) -> T {
136        clone_reactive_state_with_id::<T>(&self.id).expect("state should be present")
137    }
138
139    fn soft_get(&self) -> Option<T> {
140        clone_reactive_state_with_id::<T>(&self.id)
141    }
142}
143
144// If the accessor type is Atom, and Undo type is Allow Undo, then 
145// ensure that updates cause an undo to be appended.
146pub trait  OverloadedUpdateStateAccess<T> where T:'static {
147    fn overloaded_update<F: FnOnce(&mut T) -> ()>(&self, func: F);
148    fn overloaded_reset_to_default(&self);   
149    fn overloaded_undo(&self);
150    fn overloaded_inert_set(self, value: T);      
151    fn overloaded_set(self, value: T);
152}
153
154
155impl <T> OverloadedUpdateStateAccess<T> for ReactiveStateAccess<T,NoUndo,IsAnAtomState> where T:'static
156{
157    fn overloaded_undo(&self){
158        panic!("cannot undo this atom is not undoable");
159    }
160
161
162    fn overloaded_reset_to_default(&self){
163        // execute_reaction_nodes(&self.id);
164        (clone_reactive_state_with_id::<Reaction>(&self.id).unwrap().func)();
165        execute_reaction_nodes(&self.id);
166    
167    }
168
169    
170        
171    fn overloaded_update<F: FnOnce(&mut T) -> ()>(&self, func: F) {
172
173        update_atom_state_with_id(&self.id, func);
174
175    }
176
177    fn overloaded_inert_set(self, value: T) {
178        set_inert_atom_state_with_id(value, &self.id);
179    }
180
181    fn overloaded_set(self, value: T) {
182        set_atom_state_with_id(value, &self.id);
183    }
184}
185
186
187impl <T> OverloadedUpdateStateAccess<T> for ReactiveStateAccess<T,AllowUndo,IsAnAtomState>
188where T:Clone + 'static,
189{
190
191    fn overloaded_reset_to_default(&self){
192        (clone_reactive_state_with_id::<Reaction>(&self.id).unwrap().func)();
193        execute_reaction_nodes(&self.id);
194    }
195
196    fn overloaded_undo(&self){
197        
198        undo_atom_state::<T,AllowUndo,IsAnAtomState>(&self.id)
199    }
200
201    fn overloaded_update<F: FnOnce(&mut T) -> ()>(&self, func: F) {
202        update_atom_state_with_id_with_undo(&self.id, func);
203    }
204
205    fn overloaded_inert_set(self, value: T) {
206        set_inert_atom_state_with_id_with_undo(value, &self.id);
207    }
208
209    fn overloaded_set(self, value: T) {
210        set_atom_state_with_id_with_undo(value, &self.id);
211    }
212}
213
214
215
216// If the underlying stored type is Clone and PartialEq
217// `changed()` will return true the first time called and then false
218// if called again with the same content.
219#[derive(Clone)]
220struct ChangedWrapper<T>(T);
221
222pub trait ChangedAtomState {
223    fn changed(&self) -> bool;
224}
225
226impl<T,U,A> ChangedAtomState for ReactiveStateAccess<T,U,A>
227where
228    T: Clone + 'static + PartialEq,
229{
230    fn changed(&self) -> bool {
231        if reactive_state_exists_for_id::<ChangedWrapper<T>>(&self.id){
232            read_reactive_state_with_id::<ChangedWrapper<T>,_,_>(&self.id, |old|
233                self.get_with(|current| &old.0==current )
234            )
235        } else {
236            set_inert_atom_state_with_id(ChangedWrapper(self.get()), &self.id);
237            true
238        }
239    }
240}
241// If the underlying type provides display then so does the ReactiveStateAccess
242impl<T,U,A> std::fmt::Display for ReactiveStateAccess<T,U,A>
243where
244    T: std::fmt::Display + 'static,
245{
246    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
247        self.get_with(|t| write!(f, "{}", t))
248    }
249}
250
251use std::ops::Add;
252use std::ops::Div;
253use std::ops::Mul;
254use std::ops::Sub;
255
256impl<T,U,A> Add for ReactiveStateAccess<T,U,A>
257where
258    T: Copy + Add<Output = T> + 'static,
259{
260    type Output = T;
261
262    fn add(self, other: Self) -> Self::Output {
263        self.get_with(|s| other.get_with(|o| *o + *s))
264    }
265}
266
267impl<T,U,A> Mul for ReactiveStateAccess<T,U,A>
268where
269    T: Copy + Mul<Output = T> + 'static,
270{
271    type Output = T;
272
273    fn mul(self, other: Self) -> Self::Output {
274        self.get_with(|s| other.get_with(|o| *o * *s))
275    }
276}
277
278impl<T,U,A> Div for ReactiveStateAccess<T,U,A>
279where
280    T: Copy + Div<Output = T> + 'static,
281{
282    type Output = T;
283
284    fn div(self, other: Self) -> Self::Output {
285        self.get_with(|s| other.get_with(|o| *o / *s))
286    }
287}
288
289impl<T,U,A> Sub for ReactiveStateAccess<T,U,A>
290where
291    T: Copy + Sub<Output = T> + 'static,
292{
293    type Output = T;
294
295    fn sub(self, other: Self) -> Self::Output {
296        self.get_with(|s| other.get_with(|o| *o - *s))
297    }
298}