use crate::models::{Error, Result};
use std::path::{Path, PathBuf};
use std::time::{SystemTime, UNIX_EPOCH};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum RunStatus {
Running,
Completed,
Failed,
Aborted,
}
impl RunStatus {
pub fn as_str(&self) -> &'static str {
match self {
Self::Running => "running",
Self::Completed => "completed",
Self::Failed => "failed",
Self::Aborted => "aborted",
}
}
pub fn parse(s: &str) -> Option<Self> {
match s {
"running" => Some(Self::Running),
"completed" => Some(Self::Completed),
"failed" => Some(Self::Failed),
"aborted" => Some(Self::Aborted),
_ => None,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum StepStatus {
Pending,
Running,
Completed,
Failed,
Skipped,
}
impl StepStatus {
pub fn as_str(&self) -> &'static str {
match self {
Self::Pending => "pending",
Self::Running => "running",
Self::Completed => "completed",
Self::Failed => "failed",
Self::Skipped => "skipped",
}
}
pub fn parse(s: &str) -> Option<Self> {
match s {
"pending" => Some(Self::Pending),
"running" => Some(Self::Running),
"completed" => Some(Self::Completed),
"failed" => Some(Self::Failed),
"skipped" => Some(Self::Skipped),
_ => None,
}
}
}
#[derive(Debug, Clone)]
pub struct InstallerRun {
pub run_id: String,
pub installer_name: String,
pub installer_version: String,
pub started_at: u64,
pub completed_at: Option<u64>,
pub status: RunStatus,
pub hermetic_mode: bool,
pub lockfile_hash: Option<String>,
}
impl InstallerRun {
pub fn new(installer_name: &str, installer_version: &str) -> Self {
let run_id = generate_run_id();
let started_at = current_timestamp();
Self {
run_id,
installer_name: installer_name.to_string(),
installer_version: installer_version.to_string(),
started_at,
completed_at: None,
status: RunStatus::Running,
hermetic_mode: false,
lockfile_hash: None,
}
}
pub fn new_hermetic(
installer_name: &str,
installer_version: &str,
lockfile_hash: &str,
) -> Self {
let mut run = Self::new(installer_name, installer_version);
run.hermetic_mode = true;
run.lockfile_hash = Some(lockfile_hash.to_string());
run
}
pub fn complete(&mut self) {
self.status = RunStatus::Completed;
self.completed_at = Some(current_timestamp());
}
pub fn fail(&mut self) {
self.status = RunStatus::Failed;
self.completed_at = Some(current_timestamp());
}
}
#[derive(Debug, Clone)]
pub struct StepCheckpoint {
pub run_id: String,
pub step_id: String,
pub status: StepStatus,
pub started_at: Option<u64>,
pub completed_at: Option<u64>,
pub duration_ms: Option<u64>,
pub state_snapshot: Option<String>,
pub output_log: Option<String>,
pub error_message: Option<String>,
}
impl StepCheckpoint {
pub fn new(run_id: &str, step_id: &str) -> Self {
Self {
run_id: run_id.to_string(),
step_id: step_id.to_string(),
status: StepStatus::Pending,
started_at: None,
completed_at: None,
duration_ms: None,
state_snapshot: None,
output_log: None,
error_message: None,
}
}
pub fn start(&mut self) {
self.status = StepStatus::Running;
self.started_at = Some(current_timestamp());
}
pub fn complete(&mut self, output: Option<String>) {
self.status = StepStatus::Completed;
self.completed_at = Some(current_timestamp());
self.output_log = output;
if let (Some(start), Some(end)) = (self.started_at, self.completed_at) {
self.duration_ms = Some((end - start) * 1000);
}
}
pub fn fail(&mut self, error: &str) {
self.status = StepStatus::Failed;
self.completed_at = Some(current_timestamp());
self.error_message = Some(error.to_string());
if let (Some(start), Some(end)) = (self.started_at, self.completed_at) {
self.duration_ms = Some((end - start) * 1000);
}
}
pub fn skip(&mut self) {
self.status = StepStatus::Skipped;
}
}
#[derive(Debug, Clone)]
pub struct StateFile {
pub run_id: String,
pub step_id: String,
pub file_path: PathBuf,
pub content_hash: String,
pub backed_up_at: Option<u64>,
pub backup_path: Option<PathBuf>,
}
impl StateFile {
pub fn new(run_id: &str, step_id: &str, file_path: &Path, content_hash: &str) -> Self {
Self {
run_id: run_id.to_string(),
step_id: step_id.to_string(),
file_path: file_path.to_path_buf(),
content_hash: content_hash.to_string(),
backed_up_at: None,
backup_path: None,
}
}
pub fn set_backup(&mut self, backup_path: &Path) {
self.backed_up_at = Some(current_timestamp());
self.backup_path = Some(backup_path.to_path_buf());
}
}
#[derive(Debug)]
pub struct CheckpointStore {
checkpoint_dir: PathBuf,
current_run: Option<InstallerRun>,
steps: Vec<StepCheckpoint>,
state_files: Vec<StateFile>,
}
include!("checkpoint_checkpointstore.rs");