use alloc::{collections::BTreeMap, string::String, vec::Vec};
use semver::Version;
use crate::{ComponentId, FlowId, NodeId, PackId, SessionKey};
#[cfg(feature = "schemars")]
use schemars::JsonSchema;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
#[cfg(all(feature = "serde", feature = "time"))]
use serde_with::serde_as;
#[cfg(feature = "time")]
use time::OffsetDateTime;
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))]
#[cfg_attr(feature = "schemars", derive(JsonSchema))]
pub enum RunStatus {
Success,
PartialFailure,
Failure,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))]
#[cfg_attr(feature = "schemars", derive(JsonSchema))]
pub enum NodeStatus {
Ok,
Skipped,
Error,
}
#[derive(Clone, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "schemars", derive(JsonSchema))]
pub struct NodeSummary {
pub node_id: NodeId,
pub component: ComponentId,
pub status: NodeStatus,
pub duration_ms: u64,
}
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "schemars", derive(JsonSchema))]
pub struct TranscriptOffset {
pub start: u64,
pub end: u64,
}
#[derive(Clone, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "schemars", derive(JsonSchema))]
pub struct NodeFailure {
pub code: String,
pub message: String,
#[cfg_attr(
feature = "serde",
serde(default, skip_serializing_if = "BTreeMap::is_empty")
)]
pub details: BTreeMap<String, String>,
#[cfg_attr(
feature = "serde",
serde(default, skip_serializing_if = "Vec::is_empty")
)]
pub transcript_offsets: Vec<TranscriptOffset>,
#[cfg_attr(
feature = "serde",
serde(default, skip_serializing_if = "Vec::is_empty")
)]
pub log_paths: Vec<String>,
}
#[cfg(feature = "time")]
#[cfg_attr(feature = "serde", serde_as)]
#[derive(Clone, Debug, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "schemars", derive(JsonSchema))]
pub struct RunResult {
pub session_id: SessionKey,
pub pack_id: PackId,
#[cfg_attr(
feature = "serde",
serde_as(as = "serde_with::formats::DisplayFromStr")
)]
#[cfg_attr(
feature = "schemars",
schemars(with = "String", description = "SemVer version")
)]
pub pack_version: Version,
pub flow_id: FlowId,
#[cfg_attr(
feature = "schemars",
schemars(with = "String", description = "RFC3339 timestamp (UTC)")
)]
pub started_at_utc: OffsetDateTime,
#[cfg_attr(
feature = "schemars",
schemars(with = "String", description = "RFC3339 timestamp (UTC)")
)]
pub finished_at_utc: OffsetDateTime,
pub status: RunStatus,
#[cfg_attr(
feature = "serde",
serde(default, skip_serializing_if = "Vec::is_empty")
)]
pub node_summaries: Vec<NodeSummary>,
#[cfg_attr(
feature = "serde",
serde(default, skip_serializing_if = "Vec::is_empty")
)]
pub failures: Vec<NodeFailure>,
#[cfg_attr(
feature = "serde",
serde(default, skip_serializing_if = "Option::is_none")
)]
pub artifacts_dir: Option<String>,
}
#[cfg(feature = "time")]
impl RunResult {
pub fn duration_ms(&self) -> u64 {
let duration = self.finished_at_utc - self.started_at_utc;
duration.whole_milliseconds().max(0) as u64
}
}