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}