1use std::sync::{Arc, Mutex, MutexGuard};
2
3use thiserror::Error;
4
5use crate::RunEvent;
6
7#[derive(Clone, Debug, Error, Eq, PartialEq)]
9#[error("journal error: {message}")]
10pub struct JournalError {
11 pub message: String,
13}
14
15pub trait Journal: Send + Sync {
17 fn record(&self, event: &RunEvent) -> Result<(), JournalError>;
24}
25
26#[derive(Clone, Debug, Default)]
28pub struct InMemoryJournal {
29 events: Arc<Mutex<Vec<RunEvent>>>,
30}
31
32impl InMemoryJournal {
33 pub fn new() -> Self {
35 Self::default()
36 }
37
38 pub fn events(&self) -> Vec<RunEvent> {
40 self.lock_events().clone()
41 }
42
43 pub fn len(&self) -> usize {
45 self.lock_events().len()
46 }
47
48 pub fn is_empty(&self) -> bool {
50 self.lock_events().is_empty()
51 }
52
53 pub fn push(&self, event: RunEvent) {
55 self.lock_events().push(event);
56 }
57
58 fn lock_events(&self) -> MutexGuard<'_, Vec<RunEvent>> {
59 self.events
60 .lock()
61 .unwrap_or_else(std::sync::PoisonError::into_inner)
62 }
63}
64
65impl Journal for InMemoryJournal {
66 fn record(&self, event: &RunEvent) -> Result<(), JournalError> {
67 self.push(event.clone());
68 Ok(())
69 }
70}
71
72#[cfg(test)]
73mod tests {
74 use super::{InMemoryJournal, Journal};
75 use crate::{EventFactory, LifecycleEvent, RunEventKind, RunId};
76
77 #[test]
78 fn clones_share_the_same_event_log() {
79 let journal = InMemoryJournal::new();
80 let clone = journal.clone();
81 let event = EventFactory::new(RunId::new(), None)
82 .emit(RunEventKind::Lifecycle(LifecycleEvent::Started), None);
83
84 journal.record(&event).unwrap();
85
86 assert_eq!(clone.events(), vec![event]);
87 }
88}