fmodel_rust/
view.rs

1#[cfg(feature = "not-send-futures")]
2use std::rc::Rc;
3#[cfg(not(feature = "not-send-futures"))]
4use std::sync::Arc;
5
6use crate::{EvolveFunction, InitialStateFunction, Sum, View3, View4, View5, View6};
7
8/// [View] represents the event handling algorithm, responsible for translating the events into denormalized state, which is more adequate for querying.
9/// It has two generic parameters `S`/State, `E`/Event , representing the type of the values that View may contain or use.
10/// `'a` is used as a lifetime parameter, indicating that all references contained within the struct (e.g., references within the function closures) must have a lifetime that is at least as long as 'a.
11///
12/// ## Example
13/// ```
14/// use fmodel_rust::view::View;
15///
16/// fn view<'a>() -> View<'a, OrderViewState, OrderEvent> {
17///     View {
18///        // Exhaustive pattern matching is used to handle the events (modeled as Enum - SUM/OR type).
19///        evolve: Box::new(|state, event| {
20///             let mut new_state = state.clone();
21///             match event {
22///                OrderEvent::Created(created_event) => {
23///                     new_state.order_id = created_event.order_id;
24///                     new_state.customer_name = created_event.customer_name.to_owned();
25///                     new_state.items = created_event.items.to_owned();
26///                 }
27///                 OrderEvent::Updated(updated_event) => {
28///                     new_state.items = updated_event.updated_items.to_owned();
29///                 }
30///                 OrderEvent::Cancelled(_) => {
31///                     new_state.is_cancelled = true;
32///                 }
33///             }
34///             new_state
35///         }),
36///         initial_state: Box::new(|| OrderViewState {
37///             order_id: 0,
38///             customer_name: "".to_string(),
39///             items: Vec::new(),
40///             is_cancelled: false,
41///         }),
42///     }
43/// }
44///
45/// #[derive(Debug)]
46/// pub enum OrderEvent {
47///     Created(OrderCreatedEvent),
48///     Updated(OrderUpdatedEvent),
49///     Cancelled(OrderCancelledEvent),
50/// }
51///
52/// #[derive(Debug)]
53/// pub struct OrderCreatedEvent {
54///     pub order_id: u32,
55///     pub customer_name: String,
56///     pub items: Vec<String>,
57/// }
58///
59/// #[derive(Debug)]
60/// pub struct OrderUpdatedEvent {
61///     pub order_id: u32,
62///     pub updated_items: Vec<String>,
63/// }
64///
65/// #[derive(Debug)]
66/// pub struct OrderCancelledEvent {
67///     pub order_id: u32,
68/// }
69///
70/// #[derive(Debug, Clone)]
71/// struct OrderViewState {
72///     order_id: u32,
73///     customer_name: String,
74///     items: Vec<String>,
75///     is_cancelled: bool,
76/// }
77///
78/// let view: View<OrderViewState, OrderEvent> = view();
79/// let order_created_event = OrderEvent::Created(OrderCreatedEvent {
80///     order_id: 1,
81///     customer_name: "John Doe".to_string(),
82///     items: vec!["Item 1".to_string(), "Item 2".to_string()],
83/// });
84/// let new_state = (view.evolve)(&(view.initial_state)(), &order_created_event);
85/// ```
86pub struct View<'a, S: 'a, E: 'a> {
87    /// The `evolve` function is the main state evolution algorithm.
88    pub evolve: EvolveFunction<'a, S, E>,
89    /// The `initial_state` function is the initial state.
90    pub initial_state: InitialStateFunction<'a, S>,
91}
92
93impl<'a, S, E> View<'a, S, E> {
94    /// Maps the View over the S/State type parameter.
95    /// Creates a new instance of [View]`<S2, E>`.
96    #[cfg(not(feature = "not-send-futures"))]
97    pub fn map_state<S2, F1, F2>(self, f1: F1, f2: F2) -> View<'a, S2, E>
98    where
99        F1: Fn(&S2) -> S + Send + Sync + 'a,
100        F2: Fn(&S) -> S2 + Send + Sync + 'a,
101    {
102        let f1 = Arc::new(f1);
103        let f2 = Arc::new(f2);
104
105        let new_evolve = {
106            let f2 = Arc::clone(&f2);
107            Box::new(move |s2: &S2, e: &E| {
108                let s = f1(s2);
109                f2(&(self.evolve)(&s, e))
110            })
111        };
112
113        let new_initial_state = { Box::new(move || f2(&(self.initial_state)())) };
114
115        View {
116            evolve: new_evolve,
117            initial_state: new_initial_state,
118        }
119    }
120
121    /// Maps the View over the S/State type parameter.
122    /// Creates a new instance of [View]`<S2, E>`.
123    #[cfg(feature = "not-send-futures")]
124    pub fn map_state<S2, F1, F2>(self, f1: F1, f2: F2) -> View<'a, S2, E>
125    where
126        F1: Fn(&S2) -> S + 'a,
127        F2: Fn(&S) -> S2 + 'a,
128    {
129        let f1 = Rc::new(f1);
130        let f2 = Rc::new(f2);
131
132        let new_evolve = {
133            let f2 = Rc::clone(&f2);
134            Box::new(move |s2: &S2, e: &E| {
135                let s = f1(s2);
136                f2(&(self.evolve)(&s, e))
137            })
138        };
139
140        let new_initial_state = { Box::new(move || f2(&(self.initial_state)())) };
141
142        View {
143            evolve: new_evolve,
144            initial_state: new_initial_state,
145        }
146    }
147
148    /// Maps the View over the E/Event type parameter.
149    /// Creates a new instance of [View]`<S, E2>`.
150    #[cfg(not(feature = "not-send-futures"))]
151    pub fn map_event<E2, F>(self, f: F) -> View<'a, S, E2>
152    where
153        F: Fn(&E2) -> E + Send + Sync + 'a,
154    {
155        let new_evolve = Box::new(move |s: &S, e2: &E2| {
156            let e = f(e2);
157            (self.evolve)(s, &e)
158        });
159
160        let new_initial_state = Box::new(move || (self.initial_state)());
161
162        View {
163            evolve: new_evolve,
164            initial_state: new_initial_state,
165        }
166    }
167
168    /// Maps the View over the E/Event type parameter.
169    /// Creates a new instance of [View]`<S, E2>`.
170    #[cfg(feature = "not-send-futures")]
171    pub fn map_event<E2, F>(self, f: F) -> View<'a, S, E2>
172    where
173        F: Fn(&E2) -> E + 'a,
174    {
175        let new_evolve = Box::new(move |s: &S, e2: &E2| {
176            let e = f(e2);
177            (self.evolve)(s, &e)
178        });
179
180        let new_initial_state = Box::new(move || (self.initial_state)());
181
182        View {
183            evolve: new_evolve,
184            initial_state: new_initial_state,
185        }
186    }
187
188    /// Combines two views into one.
189    /// Creates a new instance of a View by combining two views of type `S`, `E` and `S2`, `E2` into a new view of type `(S, S2)`, `Sum<E, E2>`
190    /// Combines two views that operate on different event types (`E`` and `E2``) into a new view operating on `Sum<E, E2>`
191    #[deprecated(
192        since = "0.8.0",
193        note = "Use the `merge` function instead. This ensures all your views can subscribe to all `Event`/`E` in the system."
194    )]
195    pub fn combine<S2, E2>(self, view2: View<'a, S2, E2>) -> View<'a, (S, S2), Sum<E, E2>>
196    where
197        S: Clone,
198        S2: Clone,
199    {
200        let new_evolve = Box::new(move |s: &(S, S2), e: &Sum<E, E2>| match e {
201            Sum::First(e) => {
202                let s1 = &s.0;
203                let new_state = (self.evolve)(s1, e);
204                (new_state, s.1.to_owned())
205            }
206            Sum::Second(e) => {
207                let s2 = &s.1;
208                let new_state = (view2.evolve)(s2, e);
209                (s.0.to_owned(), new_state)
210            }
211        });
212
213        let new_initial_state = Box::new(move || {
214            let s1 = (self.initial_state)();
215            let s2 = (view2.initial_state)();
216            (s1, s2)
217        });
218
219        View {
220            evolve: new_evolve,
221            initial_state: new_initial_state,
222        }
223    }
224
225    /// Merges two views into one.
226    /// Creates a new instance of a View by merging two views of type `S`, `E` and `S2`, `E` into a new view of type `(S, S2)`, `E`
227    /// Similar to `combine`, but the event type is the same for both views.
228    /// This ensures all your views can subscribe to all `Event`/`E` in the system.
229    pub fn merge<S2>(self, view2: View<'a, S2, E>) -> View<'a, (S, S2), E>
230    where
231        S: Clone,
232        S2: Clone,
233    {
234        let new_evolve = Box::new(move |s: &(S, S2), e: &E| {
235            let s1 = &s.0;
236            let s2 = &s.1;
237
238            let new_state = (self.evolve)(s1, e);
239            let new_state2 = (view2.evolve)(s2, e);
240            (new_state, new_state2)
241        });
242
243        let new_initial_state = Box::new(move || {
244            let s1 = (self.initial_state)();
245            let s2 = (view2.initial_state)();
246            (s1, s2)
247        });
248
249        View {
250            evolve: new_evolve,
251            initial_state: new_initial_state,
252        }
253    }
254
255    /// Merges three views into one.
256    pub fn merge3<S2, S3>(
257        self,
258        view2: View<'a, S2, E>,
259        view3: View<'a, S3, E>,
260    ) -> View3<'a, S, S2, S3, E>
261    where
262        S: Clone,
263        S2: Clone,
264        S3: Clone,
265    {
266        self.merge(view2).merge(view3).map_state(
267            |s: &(S, S2, S3)| ((s.0.clone(), s.1.clone()), s.2.clone()),
268            |s: &((S, S2), S3)| (s.0 .0.clone(), s.0 .1.clone(), s.1.clone()),
269        )
270    }
271
272    /// Merges four views into one.
273    pub fn merge4<S2, S3, S4>(
274        self,
275        view2: View<'a, S2, E>,
276        view3: View<'a, S3, E>,
277        view4: View<'a, S4, E>,
278    ) -> View4<'a, S, S2, S3, S4, E>
279    where
280        S: Clone,
281        S2: Clone,
282        S3: Clone,
283        S4: Clone,
284    {
285        self.merge(view2).merge(view3).merge(view4).map_state(
286            |s: &(S, S2, S3, S4)| (((s.0.clone(), s.1.clone()), s.2.clone()), s.3.clone()),
287            |s: &(((S, S2), S3), S4)| {
288                (
289                    s.0 .0 .0.clone(),
290                    s.0 .0 .1.clone(),
291                    s.0 .1.clone(),
292                    s.1.clone(),
293                )
294            },
295        )
296    }
297
298    #[allow(clippy::type_complexity)]
299    /// Merges five views into one.
300    pub fn merge5<S2, S3, S4, S5>(
301        self,
302        view2: View<'a, S2, E>,
303        view3: View<'a, S3, E>,
304        view4: View<'a, S4, E>,
305        view5: View<'a, S5, E>,
306    ) -> View5<'a, S, S2, S3, S4, S5, E>
307    where
308        S: Clone,
309        S2: Clone,
310        S3: Clone,
311        S4: Clone,
312        S5: Clone,
313    {
314        self.merge(view2)
315            .merge(view3)
316            .merge(view4)
317            .merge(view5)
318            .map_state(
319                |s: &(S, S2, S3, S4, S5)| {
320                    (
321                        (((s.0.clone(), s.1.clone()), s.2.clone()), s.3.clone()),
322                        s.4.clone(),
323                    )
324                },
325                |s: &((((S, S2), S3), S4), S5)| {
326                    (
327                        s.0 .0 .0 .0.clone(),
328                        s.0 .0 .0 .1.clone(),
329                        s.0 .0 .1.clone(),
330                        s.0 .1.clone(),
331                        s.1.clone(),
332                    )
333                },
334            )
335    }
336
337    #[allow(clippy::type_complexity)]
338    /// Merges six views into one.
339    pub fn merge6<S2, S3, S4, S5, S6>(
340        self,
341        view2: View<'a, S2, E>,
342        view3: View<'a, S3, E>,
343        view4: View<'a, S4, E>,
344        view5: View<'a, S5, E>,
345        view6: View<'a, S6, E>,
346    ) -> View6<'a, S, S2, S3, S4, S5, S6, E>
347    where
348        S: Clone,
349        S2: Clone,
350        S3: Clone,
351        S4: Clone,
352        S5: Clone,
353        S6: Clone,
354    {
355        self.merge(view2)
356            .merge(view3)
357            .merge(view4)
358            .merge(view5)
359            .merge(view6)
360            .map_state(
361                |s: &(S, S2, S3, S4, S5, S6)| {
362                    (
363                        (
364                            (((s.0.clone(), s.1.clone()), s.2.clone()), s.3.clone()),
365                            s.4.clone(),
366                        ),
367                        s.5.clone(),
368                    )
369                },
370                |s: &(((((S, S2), S3), S4), S5), S6)| {
371                    (
372                        s.0 .0 .0 .0 .0.clone(),
373                        s.0 .0 .0 .0 .1.clone(),
374                        s.0 .0 .0 .1.clone(),
375                        s.0 .0 .1.clone(),
376                        s.0 .1.clone(),
377                        s.1.clone(),
378                    )
379                },
380            )
381    }
382}
383
384/// Formalizes the `State Computation` algorithm for the `view` to handle events based on the current state, and produce new state.
385pub trait ViewStateComputation<E, S> {
386    /// Computes new state based on the current state and the events.
387    fn compute_new_state(&self, current_state: Option<S>, events: &[&E]) -> S;
388}
389
390impl<S, E> ViewStateComputation<E, S> for View<'_, S, E> {
391    /// Computes new state based on the current state and the events.
392    fn compute_new_state(&self, current_state: Option<S>, events: &[&E]) -> S {
393        let effective_current_state = current_state.unwrap_or_else(|| (self.initial_state)());
394        events.iter().fold(effective_current_state, |state, event| {
395            (self.evolve)(&state, event)
396        })
397    }
398}