fmodel_rust/
view.rs

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