use anyhow::Result;
use std::collections::HashMap;
use std::path::PathBuf;
use std::sync::Arc;
pub use xchecker_packet::{BudgetUsage, Packet};
use xchecker_redaction::SecretRedactor;
use xchecker_selectors::Selectors;
use xchecker_status::artifact::Artifact;
pub use xchecker_utils::types::PhaseId;
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum NextStep {
Continue,
Rewind { to: PhaseId },
#[allow(dead_code)] Complete,
}
#[derive(Debug, Clone)]
pub struct PhaseContext {
pub spec_id: String,
pub spec_dir: PathBuf,
pub config: HashMap<String, String>,
#[allow(dead_code)] pub artifacts: Vec<String>,
pub selectors: Option<Selectors>,
pub strict_validation: bool,
pub redactor: Arc<SecretRedactor>,
}
#[derive(Debug, Clone, Default)]
#[allow(dead_code)] pub struct PhaseMetadata {
pub packet_hash: Option<String>,
pub budget_used: Option<BudgetUsage>,
pub duration_ms: Option<u64>,
}
#[derive(Debug, Clone)]
pub struct PhaseResult {
pub artifacts: Vec<Artifact>,
pub next_step: NextStep,
#[allow(dead_code)] pub metadata: PhaseMetadata,
}
pub trait Phase {
fn id(&self) -> PhaseId;
fn deps(&self) -> &'static [PhaseId];
#[allow(dead_code)] fn can_resume(&self) -> bool;
fn prompt(&self, ctx: &PhaseContext) -> String;
fn make_packet(&self, ctx: &PhaseContext) -> Result<Packet>;
fn postprocess(&self, raw: &str, ctx: &PhaseContext) -> Result<PhaseResult>;
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_next_step_variants() {
let continue_step = NextStep::Continue;
assert!(matches!(continue_step, NextStep::Continue));
let rewind_step = NextStep::Rewind {
to: PhaseId::Requirements,
};
assert!(matches!(rewind_step, NextStep::Rewind { .. }));
let complete_step = NextStep::Complete;
assert!(matches!(complete_step, NextStep::Complete));
}
#[test]
fn test_phase_context_creation() {
let ctx = PhaseContext {
spec_id: "test-spec".to_string(),
spec_dir: PathBuf::from("/tmp/test"),
config: HashMap::new(),
artifacts: Vec::new(),
selectors: None,
strict_validation: false,
redactor: Arc::new(SecretRedactor::default()),
};
assert_eq!(ctx.spec_id, "test-spec");
assert!(!ctx.strict_validation);
}
#[test]
fn test_phase_metadata_default() {
let metadata = PhaseMetadata::default();
assert_eq!(metadata.packet_hash, None);
assert!(metadata.budget_used.is_none());
assert_eq!(metadata.duration_ms, None);
}
}