use std::str::FromStr;
use chrono::{DateTime, Utc};
use uuid::Uuid;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct RunFootprint {
pub metadata: RunMetadata,
pub at: DateTime<Utc>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct RunMetadata {
pub run_id: Uuid,
pub state: RunState,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum RunState {
Fresh,
Running,
Stopped,
Completed,
Aborted,
}
impl RunFootprint {
pub fn new(run_id: Uuid, state: RunState) -> Self {
let metadata = RunMetadata::new(run_id, state);
Self::from(metadata)
}
pub fn at(run_id: Uuid, state: RunState, at: DateTime<Utc>) -> Self {
let metadata = RunMetadata::new(run_id, state);
Self { metadata, at }
}
}
impl From<RunMetadata> for RunFootprint {
fn from(metadata: RunMetadata) -> Self {
Self {
metadata,
at: Utc::now(),
}
}
}
impl RunMetadata {
pub fn new(run_id: Uuid, state: RunState) -> Self {
Self { run_id, state }
}
}
impl RunState {
pub(crate) const RECORDED: [Self; 4] =
[Self::Running, Self::Stopped, Self::Completed, Self::Aborted];
pub(crate) const ENDED: [Self; 3] = [Self::Stopped, Self::Completed, Self::Aborted];
}
impl Default for RunMetadata {
fn default() -> Self {
Self {
run_id: Uuid::new_v4(),
state: RunState::Fresh,
}
}
}
impl std::fmt::Display for RunState {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let state_str = match self {
RunState::Fresh => "fresh",
RunState::Running => "running",
RunState::Stopped => "stopped",
RunState::Completed => "completed",
RunState::Aborted => "aborted",
};
write!(f, "{}", state_str)
}
}
impl FromStr for RunState {
type Err = String;
fn from_str(s: &str) -> Result<Self, Self::Err> {
if s.eq_ignore_ascii_case("fresh") {
Ok(RunState::Fresh)
} else if s.eq_ignore_ascii_case("running") {
Ok(RunState::Running)
} else if s.eq_ignore_ascii_case("stopped") {
Ok(RunState::Stopped)
} else if s.eq_ignore_ascii_case("completed") {
Ok(RunState::Completed)
} else if s.eq_ignore_ascii_case("aborted") {
Ok(RunState::Aborted)
} else {
Err(s.to_owned())
}
}
}
#[cfg(test)]
mod tests {
use rstest::rstest;
use super::*;
#[rstest]
#[case::fresh(RunState::Fresh)]
#[case::running(RunState::Running)]
#[case::stopped(RunState::Stopped)]
#[case::completed(RunState::Completed)]
#[case::aborted(RunState::Aborted)]
fn test_run_state_round_trip(#[case] state: RunState) {
assert_eq!(RunState::from_str(&state.to_string()), Ok(state));
}
#[test]
fn test_parse_paused_is_rejected() {
assert_eq!(RunState::from_str("paused"), Err("paused".to_owned()));
}
}