use std::time::{Duration, SystemTime};
use super::{
CommandExecutionContext, CommandIdentity, CommandRecordId, CommandResultSummary, CommandSource,
DEFAULT_STREAM_LIMIT, ExitStatusSummary, OutputRetention, StreamSummary,
};
use crate::{ExecutionMode, JjCommandSpec, RefreshPlan, SafetyClass};
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct CommandRecord {
pub id: CommandRecordId,
pub command: CommandIdentity,
pub source: CommandSource,
pub context: CommandExecutionContext,
pub timing: CommandTiming,
pub result: CommandResultSummary,
pub retention: OutputRetention,
pub refresh: RefreshPlan,
pub safety: SafetyClass,
pub execution_mode: ExecutionMode,
pub operation_id: Option<String>,
}
impl CommandRecord {
pub(super) fn started(id: CommandRecordId, input: CommandRecordStart) -> Self {
Self {
id,
command: input.command,
source: input.source,
context: input.context,
timing: CommandTiming {
started_at: input.started_at,
ended_at: None,
duration: None,
},
result: CommandResultSummary::default(),
retention: input.retention,
refresh: input.refresh,
safety: input.safety,
execution_mode: input.execution_mode,
operation_id: None,
}
}
pub(super) fn finish(&mut self, mut finish: CommandRecordFinish) -> bool {
if self.timing.ended_at.is_some() {
return false;
}
let duration = finish
.ended_at
.duration_since(self.timing.started_at)
.unwrap_or_default();
finish.result.apply_retention(&self.retention);
self.timing.ended_at = Some(finish.ended_at);
self.timing.duration = Some(duration);
self.result = finish.result;
self.operation_id = finish.operation_id;
true
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct CommandRecordStart {
pub command: CommandIdentity,
pub source: CommandSource,
pub context: CommandExecutionContext,
pub started_at: SystemTime,
pub retention: OutputRetention,
pub refresh: RefreshPlan,
pub safety: SafetyClass,
pub execution_mode: ExecutionMode,
}
impl CommandRecordStart {
#[must_use]
pub fn from_spec(spec: &JjCommandSpec, source: CommandSource) -> Self {
Self {
command: CommandIdentity::from_spec(spec),
source,
context: CommandExecutionContext::from_spec(spec),
started_at: SystemTime::now(),
retention: OutputRetention::summary_only(DEFAULT_STREAM_LIMIT, DEFAULT_STREAM_LIMIT),
refresh: spec.refresh_plan(),
safety: spec.safety(),
execution_mode: spec.mode(),
}
}
#[must_use]
pub const fn with_started_at(mut self, started_at: SystemTime) -> Self {
self.started_at = started_at;
self
}
#[must_use]
pub fn with_retention(mut self, retention: OutputRetention) -> Self {
self.retention = retention;
self
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct CommandRecordFinish {
pub ended_at: SystemTime,
pub result: CommandResultSummary,
pub operation_id: Option<String>,
}
impl CommandRecordFinish {
#[must_use]
pub const fn from_result(
result: CommandResultSummary,
operation_id: Option<String>,
ended_at: SystemTime,
) -> Self {
Self {
ended_at,
result,
operation_id,
}
}
#[must_use]
pub fn from_exit_code(
code: i32,
stdout: impl AsRef<[u8]>,
stderr: impl AsRef<[u8]>,
ended_at: SystemTime,
) -> Self {
Self::from_result(
CommandResultSummary {
exit_status: Some(ExitStatusSummary::code(code)),
spawn_error: None,
stdout: StreamSummary::from_bytes(stdout.as_ref(), DEFAULT_STREAM_LIMIT),
stderr: StreamSummary::from_bytes(stderr.as_ref(), DEFAULT_STREAM_LIMIT),
},
None,
ended_at,
)
}
#[must_use]
pub fn from_spawn_error(
error: impl Into<String>,
stdout: impl AsRef<[u8]>,
stderr: impl AsRef<[u8]>,
ended_at: SystemTime,
) -> Self {
Self::from_result(
CommandResultSummary {
exit_status: None,
spawn_error: Some(error.into()),
stdout: StreamSummary::from_bytes(stdout.as_ref(), DEFAULT_STREAM_LIMIT),
stderr: StreamSummary::from_bytes(stderr.as_ref(), DEFAULT_STREAM_LIMIT),
},
None,
ended_at,
)
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct CommandTiming {
pub started_at: SystemTime,
pub ended_at: Option<SystemTime>,
pub duration: Option<Duration>,
}