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}