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}