1use chrono::{DateTime, Utc};
4
5use super::{error::EntityHydrationError, traits::*};
6
7pub type LastPersisted<'a, E> = std::slice::Iter<'a, PersistedEvent<E>>;
9
10pub struct GenericEvent<Id> {
16 pub entity_id: Id,
17 pub sequence: i32,
18 pub event: serde_json::Value,
19 pub context: Option<crate::ContextData>,
20 pub recorded_at: DateTime<Utc>,
21 pub forgettable_payload: Option<serde_json::Value>,
22}
23
24pub struct PersistedEvent<E: EsEvent> {
30 pub entity_id: <E as EsEvent>::EntityId,
32 pub recorded_at: DateTime<Utc>,
34 pub sequence: usize,
36 pub event: E,
38 pub context: Option<crate::ContextData>,
41}
42
43impl<E: Clone + EsEvent> Clone for PersistedEvent<E> {
44 fn clone(&self) -> Self {
45 PersistedEvent {
46 entity_id: self.entity_id.clone(),
47 recorded_at: self.recorded_at,
48 sequence: self.sequence,
49 event: self.event.clone(),
50 context: self.context.clone(),
51 }
52 }
53}
54
55pub struct EventWithContext<E: EsEvent> {
56 pub event: E,
57 pub context: Option<crate::ContextData>,
58}
59
60impl<E: Clone + EsEvent> Clone for EventWithContext<E> {
61 fn clone(&self) -> Self {
62 EventWithContext {
63 event: self.event.clone(),
64 context: self.context.clone(),
65 }
66 }
67}
68
69pub struct EntityEvents<T: EsEvent> {
74 pub entity_id: <T as EsEvent>::EntityId,
76 persisted_events: Vec<PersistedEvent<T>>,
78 new_events: Vec<EventWithContext<T>>,
80}
81
82impl<T: Clone + EsEvent> Clone for EntityEvents<T> {
83 fn clone(&self) -> Self {
84 Self {
85 entity_id: self.entity_id.clone(),
86 persisted_events: self.persisted_events.clone(),
87 new_events: self.new_events.clone(),
88 }
89 }
90}
91
92impl<T> EntityEvents<T>
93where
94 T: EsEvent,
95{
96 pub fn init(id: <T as EsEvent>::EntityId, initial_events: impl IntoIterator<Item = T>) -> Self {
98 let context = if <T as EsEvent>::event_context() {
99 Some(crate::EventContext::data_for_storing())
100 } else {
101 None
102 };
103 let new_events = initial_events
104 .into_iter()
105 .map(|event| EventWithContext {
106 event,
107 context: context.clone(),
108 })
109 .collect();
110 Self {
111 entity_id: id,
112 persisted_events: Vec::new(),
113 new_events,
114 }
115 }
116
117 pub fn id(&self) -> &<T as EsEvent>::EntityId {
119 &self.entity_id
120 }
121
122 pub fn entity_first_persisted_at(&self) -> Option<DateTime<Utc>> {
124 self.persisted_events.first().map(|e| e.recorded_at)
125 }
126
127 pub fn entity_last_modified_at(&self) -> Option<DateTime<Utc>> {
129 self.persisted_events.last().map(|e| e.recorded_at)
130 }
131
132 pub fn push(&mut self, event: T) {
134 let context = if <T as EsEvent>::event_context() {
135 Some(crate::EventContext::data_for_storing())
136 } else {
137 None
138 };
139 self.new_events.push(EventWithContext { event, context });
140 }
141
142 pub fn extend(&mut self, events: impl IntoIterator<Item = T>) {
144 let context = if <T as EsEvent>::event_context() {
145 Some(crate::EventContext::data_for_storing())
146 } else {
147 None
148 };
149 self.new_events
150 .extend(events.into_iter().map(|event| EventWithContext {
151 event,
152 context: context.clone(),
153 }));
154 }
155
156 pub fn any_new(&self) -> bool {
158 !self.new_events.is_empty()
159 }
160
161 pub fn len_persisted(&self) -> usize {
163 self.persisted_events.len()
164 }
165
166 pub fn iter_persisted(&self) -> impl DoubleEndedIterator<Item = &PersistedEvent<T>> + Clone {
168 self.persisted_events.iter()
169 }
170
171 pub fn last_persisted(&self, n: usize) -> LastPersisted<'_, T> {
176 let start = self.persisted_events.len().saturating_sub(n);
177 self.persisted_events[start..].iter()
178 }
179
180 pub fn iter_all(&self) -> impl DoubleEndedIterator<Item = &T> + Clone {
182 self.persisted_events
183 .iter()
184 .map(|e| &e.event)
185 .chain(self.new_events.iter().map(|e| &e.event))
186 }
187
188 pub fn load_first<E: EsEntity<Event = T>>(
192 events: impl IntoIterator<Item = GenericEvent<<T as EsEvent>::EntityId>>,
193 ) -> Result<Option<E>, EntityHydrationError> {
194 let mut current_id = None;
195 let mut current = None;
196 for e in events {
197 if current_id.is_none() {
198 current_id = Some(e.entity_id.clone());
199 current = Some(Self {
200 entity_id: e.entity_id.clone(),
201 persisted_events: Vec::new(),
202 new_events: Vec::new(),
203 });
204 }
205 if current_id.as_ref() != Some(&e.entity_id) {
206 break;
207 }
208 let cur = current.as_mut().expect("Could not get current");
209 let mut event_json = e.event;
210 if let Some(payload) = e.forgettable_payload {
211 crate::forgettable::inject_forgettable_payload(&mut event_json, payload);
212 }
213 cur.persisted_events.push(PersistedEvent {
214 entity_id: e.entity_id,
215 recorded_at: e.recorded_at,
216 sequence: e.sequence as usize,
217 event: serde_json::from_value(event_json)?,
218 context: e.context,
219 });
220 }
221 if let Some(current) = current {
222 Ok(Some(E::try_from_events(current)?))
223 } else {
224 Ok(None)
225 }
226 }
227
228 pub fn load_n<E: EsEntity<Event = T>>(
233 events: impl IntoIterator<Item = GenericEvent<<T as EsEvent>::EntityId>>,
234 n: usize,
235 ) -> Result<(Vec<E>, bool), EntityHydrationError> {
236 if n == 0 {
237 let has_more = events.into_iter().next().is_some();
242 return Ok((Vec::new(), has_more));
243 }
244 let mut ret: Vec<E> = Vec::new();
245 let mut current_id = None;
246 let mut current = None;
247 for e in events {
248 if current_id.as_ref() != Some(&e.entity_id) {
249 if let Some(current) = current.take() {
250 ret.push(E::try_from_events(current)?);
251 if ret.len() == n {
252 return Ok((ret, true));
253 }
254 }
255
256 current_id = Some(e.entity_id.clone());
257 current = Some(Self {
258 entity_id: e.entity_id.clone(),
259 persisted_events: Vec::new(),
260 new_events: Vec::new(),
261 });
262 }
263 let cur = current.as_mut().expect("Could not get current");
264 let mut event_json = e.event;
265 if let Some(payload) = e.forgettable_payload {
266 crate::forgettable::inject_forgettable_payload(&mut event_json, payload);
267 }
268 cur.persisted_events.push(PersistedEvent {
269 entity_id: e.entity_id,
270 recorded_at: e.recorded_at,
271 sequence: e.sequence as usize,
272 event: serde_json::from_value(event_json)?,
273 context: e.context,
274 });
275 }
276 if let Some(current) = current.take() {
277 ret.push(E::try_from_events(current)?);
278 }
279 Ok((ret, false))
280 }
281
282 #[doc(hidden)]
283 pub fn iter_new_events(&self) -> impl Iterator<Item = &EventWithContext<T>> {
284 self.new_events.iter()
285 }
286
287 #[doc(hidden)]
288 pub fn mark_new_events_persisted_at(
289 &mut self,
290 recorded_at: chrono::DateTime<chrono::Utc>,
291 ) -> usize {
292 let n = self.new_events.len();
293 let offset = self.persisted_events.len() + 1;
294 self.persisted_events
295 .extend(
296 self.new_events
297 .drain(..)
298 .enumerate()
299 .map(|(i, event)| PersistedEvent {
300 entity_id: self.entity_id.clone(),
301 recorded_at,
302 sequence: i + offset,
303 event: event.event,
304 context: event.context,
305 }),
306 );
307 n
308 }
309
310 #[doc(hidden)]
311 pub fn new_event_types(&self) -> Vec<String> {
312 self.new_events
313 .iter()
314 .map(|event| event.event.event_type().to_string())
315 .collect()
316 }
317
318 #[doc(hidden)]
319 pub fn serialize_new_events(&self) -> Vec<serde_json::Value> {
320 self.new_events
321 .iter()
322 .map(|event| serde_json::to_value(&event.event).expect("Failed to serialize event"))
323 .collect()
324 }
325
326 #[doc(hidden)]
332 pub fn forget_and_take(&mut self, mut forget_fn: impl FnMut(&mut T)) -> Self {
333 for persisted in &mut self.persisted_events {
334 forget_fn(&mut persisted.event);
335 }
336 let entity_id = self.entity_id.clone();
337 std::mem::replace(
338 self,
339 Self {
340 entity_id,
341 persisted_events: Vec::new(),
342 new_events: Vec::new(),
343 },
344 )
345 }
346
347 #[doc(hidden)]
348 pub fn serialize_new_event_contexts(&self) -> Option<Vec<crate::ContextData>> {
349 if <T as EsEvent>::event_context() {
350 let contexts = self
351 .new_events
352 .iter()
353 .map(|event| event.context.clone().expect("Missing context"))
354 .collect();
355
356 Some(contexts)
357 } else {
358 None
359 }
360 }
361}
362
363#[cfg(test)]
364mod tests {
365 use super::*;
366 use proptest::prelude::*;
367 use uuid::Uuid;
368
369 fn valid_event(id: Uuid, sequence: i32, name: &str) -> GenericEvent<Uuid> {
371 GenericEvent {
372 entity_id: id,
373 sequence,
374 event: serde_json::to_value(DummyEntityEvent::Created(name.to_string()))
375 .expect("could not serialize"),
376 context: None,
377 recorded_at: chrono::Utc::now(),
378 forgettable_payload: None,
379 }
380 }
381
382 fn json_value() -> impl Strategy<Value = serde_json::Value> {
386 let scalar = prop_oneof![
387 Just(serde_json::Value::Null),
388 any::<bool>().prop_map(serde_json::Value::Bool),
389 any::<i64>().prop_map(serde_json::Value::from),
390 any::<f64>().prop_map(serde_json::Value::from),
391 ".{0,15}".prop_map(serde_json::Value::String),
392 ]
393 .boxed();
394 let nested = prop_oneof![
395 proptest::collection::vec(scalar.clone(), 0..4).prop_map(serde_json::Value::Array),
396 proptest::collection::vec((".{0,6}", scalar.clone()), 0..4).prop_map(|pairs| {
397 let mut m = serde_json::Map::new();
398 for (k, v) in pairs {
399 m.insert(k, v);
400 }
401 serde_json::Value::Object(m)
402 },),
403 ];
404 prop_oneof![scalar, nested]
405 }
406
407 #[derive(Debug, serde::Serialize, serde::Deserialize)]
408 enum DummyEntityEvent {
409 Created(String),
410 }
411
412 impl EsEvent for DummyEntityEvent {
413 type EntityId = Uuid;
414 fn event_context() -> bool {
415 true
416 }
417 fn event_type(&self) -> &'static str {
418 match self {
419 Self::Created(_) => "created",
420 }
421 }
422 }
423
424 struct DummyEntity {
425 name: String,
426
427 events: EntityEvents<DummyEntityEvent>,
428 }
429
430 impl EsEntity for DummyEntity {
431 type Event = DummyEntityEvent;
432 type New = NewDummyEntity;
433
434 fn events_mut(&mut self) -> &mut EntityEvents<DummyEntityEvent> {
435 &mut self.events
436 }
437 fn events(&self) -> &EntityEvents<DummyEntityEvent> {
438 &self.events
439 }
440 }
441
442 impl TryFromEvents<DummyEntityEvent> for DummyEntity {
443 fn try_from_events(
444 events: EntityEvents<DummyEntityEvent>,
445 ) -> Result<Self, EntityHydrationError> {
446 let name = events
447 .iter_persisted()
448 .map(|e| match &e.event {
449 DummyEntityEvent::Created(name) => name.clone(),
450 })
451 .next()
452 .expect("Could not find name");
453 Ok(Self { name, events })
454 }
455 }
456
457 struct NewDummyEntity {}
458
459 impl IntoEvents<DummyEntityEvent> for NewDummyEntity {
460 fn into_events(self) -> EntityEvents<DummyEntityEvent> {
461 EntityEvents::init(
462 Uuid::parse_str("00000000-0000-0000-0000-000000000000").unwrap(),
463 vec![DummyEntityEvent::Created("".to_owned())],
464 )
465 }
466 }
467
468 #[test]
469 fn load_zero_events() {
470 let generic_events = vec![];
471 let res = EntityEvents::load_first::<DummyEntity>(generic_events);
472 assert!(matches!(res, Ok(None)));
473 }
474
475 #[test]
476 fn load_first() {
477 let generic_events = vec![GenericEvent {
478 entity_id: Uuid::parse_str("00000000-0000-0000-0000-000000000001").unwrap(),
479 sequence: 1,
480 event: serde_json::to_value(DummyEntityEvent::Created("dummy-name".to_owned()))
481 .expect("Could not serialize"),
482 context: None,
483 recorded_at: chrono::Utc::now(),
484 forgettable_payload: None,
485 }];
486 let entity: DummyEntity = EntityEvents::load_first(generic_events)
487 .expect("Could not load")
488 .expect("No entity found");
489 assert!(entity.name == "dummy-name");
490 }
491
492 #[test]
493 fn load_n() {
494 let generic_events = vec![
495 GenericEvent {
496 entity_id: Uuid::parse_str("00000000-0000-0000-0000-000000000002").unwrap(),
497 sequence: 1,
498 event: serde_json::to_value(DummyEntityEvent::Created("dummy-name".to_owned()))
499 .expect("Could not serialize"),
500 context: None,
501 recorded_at: chrono::Utc::now(),
502 forgettable_payload: None,
503 },
504 GenericEvent {
505 entity_id: Uuid::parse_str("00000000-0000-0000-0000-000000000003").unwrap(),
506 sequence: 1,
507 event: serde_json::to_value(DummyEntityEvent::Created("other-name".to_owned()))
508 .expect("Could not serialize"),
509 context: None,
510 recorded_at: chrono::Utc::now(),
511 forgettable_payload: None,
512 },
513 ];
514 let (entity, more): (Vec<DummyEntity>, _) =
515 EntityEvents::load_n(generic_events, 2).expect("Could not load");
516 assert!(!more);
517 assert_eq!(entity.len(), 2);
518 }
519
520 #[test]
521 fn last_persisted_does_not_panic_when_n_exceeds_len() {
522 let generic_events = vec![GenericEvent {
523 entity_id: Uuid::parse_str("00000000-0000-0000-0000-000000000004").unwrap(),
524 sequence: 1,
525 event: serde_json::to_value(DummyEntityEvent::Created("dummy".to_owned()))
526 .expect("Could not serialize"),
527 context: None,
528 recorded_at: chrono::Utc::now(),
529 forgettable_payload: None,
530 }];
531 let entity: DummyEntity = EntityEvents::load_first(generic_events)
532 .expect("Could not load")
533 .expect("No entity found");
534 let events = entity.events();
535
536 assert_eq!(events.last_persisted(1).count(), 1);
538 assert_eq!(events.last_persisted(10).count(), 1);
540 assert_eq!(events.last_persisted(0).count(), 0);
542 }
543
544 proptest! {
545 #[test]
546 fn load_first_empty_input_returns_none(_ in Just(())) {
547 let res = EntityEvents::<DummyEntityEvent>::load_first::<DummyEntity>(vec![]);
548 prop_assert!(matches!(res, Ok(None)));
549 }
550
551 #[test]
552 fn load_first_non_empty_valid_hydrates(
553 count in 1u8..8,
554 names in proptest::collection::vec(".{0,10}", 1..8),
555 ) {
556 let id = Uuid::nil();
557 let events: Vec<_> = (1..=count as i32)
558 .zip(names.iter())
559 .map(|(s, n)| valid_event(id, s, n))
560 .collect();
561 let res = EntityEvents::<DummyEntityEvent>::load_first::<DummyEntity>(events);
562 prop_assert!(matches!(res, Ok(Some(_))));
563 }
564
565 #[test]
568 fn load_first_arbitrary_json_never_panics(
569 raw in proptest::collection::vec(json_value(), 0..10),
570 ) {
571 let events: Vec<_> = raw
572 .into_iter()
573 .enumerate()
574 .map(|(i, v)| GenericEvent {
575 entity_id: Uuid::nil(),
576 sequence: i as i32,
577 event: v,
578 context: None,
579 recorded_at: chrono::Utc::now(),
580 forgettable_payload: None,
581 })
582 .collect();
583 let is_empty = events.is_empty();
584 let res = EntityEvents::<DummyEntityEvent>::load_first::<DummyEntity>(events);
585 if let Ok(None) = res {
586 prop_assert!(is_empty);
587 }
588 }
589
590 #[test]
594 fn load_n_respects_limit_and_more_flag(
595 k in 1u8..8,
596 per in 1u8..4,
597 n in 1u8..12,
598 ) {
599 let mut events = Vec::new();
600 for i in 0..k {
601 let id = Uuid::from_u128(i as u128);
602 for s in 1..=per as i32 {
603 events.push(valid_event(id, s, &format!("e{i}-{s}")));
604 }
605 }
606 let (entities, has_more) =
607 EntityEvents::<DummyEntityEvent>::load_n::<DummyEntity>(events, n as usize)
608 .expect("valid events hydrate");
609 prop_assert_eq!(entities.len(), (n as usize).min(k as usize));
610 prop_assert_eq!(has_more, n < k);
611 }
612
613 #[test]
617 fn load_n_zero_returns_no_entities(k in 0u8..5, per in 1u8..3) {
618 let mut events = Vec::new();
619 for i in 0..k {
620 let id = Uuid::from_u128(i as u128);
621 for s in 1..=per as i32 {
622 events.push(valid_event(id, s, &format!("e{i}-{s}")));
623 }
624 }
625 let (entities, has_more) =
626 EntityEvents::<DummyEntityEvent>::load_n::<DummyEntity>(events, 0)
627 .expect("valid events hydrate");
628 prop_assert!(entities.is_empty());
629 prop_assert_eq!(has_more, k > 0);
630 }
631
632 #[test]
633 fn load_n_arbitrary_json_never_panics(
634 raw in proptest::collection::vec(json_value(), 0..10),
635 n in 0u8..12,
636 ) {
637 let events: Vec<_> = raw
638 .into_iter()
639 .enumerate()
640 .map(|(i, v)| GenericEvent {
641 entity_id: Uuid::nil(),
642 sequence: i as i32,
643 event: v,
644 context: None,
645 recorded_at: chrono::Utc::now(),
646 forgettable_payload: None,
647 })
648 .collect();
649 let _ = EntityEvents::<DummyEntityEvent>::load_n::<DummyEntity>(events, n as usize);
650 }
651
652 #[test]
655 fn last_persisted_clamps_for_any_n(
656 p in 1u8..8,
657 n in 0u16..12,
658 ) {
659 let id = Uuid::nil();
660 let events: Vec<_> = (1..=p as i32)
661 .map(|s| valid_event(id, s, &format!("n{s}")))
662 .collect();
663 let entity: DummyEntity =
664 EntityEvents::<DummyEntityEvent>::load_first::<DummyEntity>(events)
665 .expect("load")
666 .expect("some");
667 let count = entity.events().last_persisted(n as usize).count();
668 prop_assert_eq!(count, (n as usize).min(p as usize));
669 }
670
671 #[test]
675 fn mark_new_events_assigns_contiguous_sequences(
676 a in 0u8..5,
677 b in 0u8..5,
678 ) {
679 let id = Uuid::nil();
680 let mut events = EntityEvents::init(
681 id,
682 (0..a).map(|i| DummyEntityEvent::Created(format!("n{i}"))),
683 );
684 let now = chrono::Utc::now();
685
686 prop_assert_eq!(events.mark_new_events_persisted_at(now), a as usize);
687 prop_assert!(!events.any_new());
688 prop_assert_eq!(events.len_persisted(), a as usize);
689 let seqs: Vec<usize> = events.iter_persisted().map(|e| e.sequence).collect();
690 prop_assert_eq!(seqs, (1..=a as usize).collect::<Vec<_>>());
691
692 for i in 0..b {
693 events.push(DummyEntityEvent::Created(format!("m{i}")));
694 }
695 if b > 0 {
696 prop_assert!(events.any_new());
697 }
698 prop_assert_eq!(events.mark_new_events_persisted_at(now), b as usize);
699 prop_assert!(!events.any_new());
700 prop_assert_eq!(events.len_persisted(), (a + b) as usize);
701 let seqs: Vec<usize> = events.iter_persisted().map(|e| e.sequence).collect();
702 prop_assert_eq!(seqs, (1..=(a + b) as usize).collect::<Vec<_>>());
703 }
704 }
705}