use std::fmt;
#[derive(Debug, Clone)]
pub struct Event {
activity: String,
timestamp_ns: u64,
resource: Option<String>,
lifecycle: Option<String>,
}
impl Event {
pub fn new(activity: &str) -> Self {
Event {
activity: activity.to_owned(),
timestamp_ns: 0,
resource: None,
lifecycle: None,
}
}
#[must_use]
pub fn at_ns(mut self, ns: u64) -> Self { self.timestamp_ns = ns; self }
#[must_use]
pub fn by(mut self, resource: &str) -> Self { self.resource = Some(resource.to_owned()); self }
#[must_use]
pub fn with_lifecycle(mut self, lc: &str) -> Self { self.lifecycle = Some(lc.to_owned()); self }
pub fn activity(&self) -> &str { &self.activity }
pub fn resource(&self) -> Option<&str> { self.resource.as_deref() }
pub fn lifecycle(&self) -> Option<&str> { self.lifecycle.as_deref() }
}
#[derive(Debug, Clone)]
pub struct Trace {
case_id: String,
events: Vec<Event>,
}
impl Trace {
pub fn new(case_id: &str, events: impl IntoIterator<Item = Event>) -> Self {
Trace { case_id: case_id.to_owned(), events: events.into_iter().collect() }
}
pub fn from_events(events: impl IntoIterator<Item = Event>) -> Self {
Trace { case_id: String::new(), events: events.into_iter().collect() }
}
pub fn case_id(&self) -> &str { &self.case_id }
pub fn len(&self) -> usize { self.events.len() }
pub fn is_empty(&self) -> bool { self.events.is_empty() }
pub fn events(&self) -> &[Event] { &self.events }
#[must_use]
pub fn validate(&self) -> Result<(), EventLogRefusal> {
if self.events.is_empty() {
return Err(EventLogRefusal::EmptyTrace);
}
let stamped: Vec<u64> = self.events.iter()
.map(|e| e.timestamp_ns)
.filter(|&t| t > 0)
.collect();
for w in stamped.windows(2) {
if w[1] < w[0] {
return Err(EventLogRefusal::NonMonotonicTrace);
}
}
Ok(())
}
}
#[derive(Debug, Clone)]
pub struct EventLog {
traces: Vec<Trace>,
}
impl EventLog {
pub fn from_traces(traces: impl IntoIterator<Item = Trace>) -> Self {
EventLog { traces: traces.into_iter().collect() }
}
pub fn traces(&self) -> &[Trace] { &self.traces }
pub fn trace_count(&self) -> usize { self.traces.len() }
pub fn event_count(&self) -> usize { self.traces.iter().map(|t| t.len()).sum() }
#[must_use]
pub fn validate(&self) -> Result<(), EventLogRefusal> {
for trace in &self.traces {
trace.validate()?;
}
Ok(())
}
}
#[derive(Debug, Clone, Default)]
pub struct EventStream {
events: Vec<Event>,
}
impl EventStream {
pub fn new() -> Self { EventStream::default() }
pub fn push(&mut self, e: Event) { self.events.push(e); }
pub fn is_empty(&self) -> bool { self.events.is_empty() }
pub fn len(&self) -> usize { self.events.len() }
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum EventLogRefusal {
EmptyTrace,
NonMonotonicTrace,
}
impl fmt::Display for EventLogRefusal {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
EventLogRefusal::EmptyTrace => write!(f, "EmptyTrace"),
EventLogRefusal::NonMonotonicTrace => write!(f, "NonMonotonicTrace"),
}
}
}
impl std::error::Error for EventLogRefusal {}