fmodel_rust/
view.rs

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