use std::sync::atomic::{AtomicU64, Ordering};
use std::time::{SystemTime, UNIX_EPOCH};
use serde::{Deserialize, Serialize};
use serde_json::Value;
use crate::{EffectId, EventId, RunError, RunId, Usage};
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
pub struct EventMeta {
pub event_id: EventId,
pub sequence: u64,
pub run_id: RunId,
pub parent_run_id: Option<RunId>,
pub caused_by: Option<EventId>,
pub timestamp_ms: u64,
}
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
pub struct RunEvent {
pub meta: EventMeta,
pub kind: RunEventKind,
}
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
#[non_exhaustive]
pub enum RunEventKind {
Lifecycle(LifecycleEvent),
Effect(EffectEvent),
Child(ChildEvent),
Budget(BudgetEvent),
Domain(DomainEvent),
}
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
#[non_exhaustive]
pub enum LifecycleEvent {
Started,
Completed {
output: Value,
},
Failed {
error: RunError,
},
Cancelled,
}
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
#[non_exhaustive]
pub enum EffectEvent {
Requested {
effect_id: EffectId,
},
Started {
effect_id: EffectId,
},
Completed {
effect_id: EffectId,
output: Value,
},
Failed {
effect_id: EffectId,
error: RunError,
},
}
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
#[non_exhaustive]
pub enum ChildEvent {
Started {
child_run_id: RunId,
},
Completed {
child_run_id: RunId,
},
Failed {
child_run_id: RunId,
},
Cancelled {
child_run_id: RunId,
},
}
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
#[non_exhaustive]
pub enum BudgetEvent {
Updated {
usage: Usage,
},
}
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
pub struct DomainEvent {
pub namespace: String,
pub name: String,
pub payload: Value,
}
#[derive(Debug)]
pub struct EventFactory {
run_id: RunId,
parent_run_id: Option<RunId>,
sequence: AtomicU64,
}
impl EventFactory {
pub const fn new(run_id: RunId, parent_run_id: Option<RunId>) -> Self {
Self {
run_id,
parent_run_id,
sequence: AtomicU64::new(0),
}
}
pub fn emit(&self, kind: RunEventKind, caused_by: Option<EventId>) -> RunEvent {
let sequence = self.sequence.fetch_add(1, Ordering::Relaxed);
let timestamp_ms = SystemTime::now()
.duration_since(UNIX_EPOCH)
.map_or(0, |duration| {
u64::try_from(duration.as_millis()).unwrap_or(u64::MAX)
});
RunEvent {
meta: EventMeta {
event_id: EventId::new(),
sequence,
run_id: self.run_id,
parent_run_id: self.parent_run_id,
caused_by,
timestamp_ms,
},
kind,
}
}
}
#[cfg(test)]
mod tests {
use super::{EventFactory, LifecycleEvent, RunEventKind};
use crate::RunId;
#[test]
fn event_sequences_are_monotonic_and_causal() {
let factory = EventFactory::new(RunId::new(), None);
let started = factory.emit(RunEventKind::Lifecycle(LifecycleEvent::Started), None);
let completed = factory.emit(
RunEventKind::Lifecycle(LifecycleEvent::Completed {
output: serde_json::json!("done"),
}),
Some(started.meta.event_id),
);
assert_eq!(started.meta.sequence, 0);
assert_eq!(completed.meta.sequence, 1);
assert_eq!(completed.meta.caused_by, Some(started.meta.event_id));
}
}