use std::path::PathBuf;
#[derive(Debug, Clone, PartialEq, Eq, Default)]
pub struct AdoptionPlan {
pub findings: Vec<DetectedGate>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct DetectedGate {
pub gate_type: GateType,
pub source_path: PathBuf,
pub proposed_checks: Vec<ProposedCheck>,
pub chain_support: ChainSupport,
pub manual_chain_instructions: Option<String>,
pub warnings: Vec<String>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum HookStage {
PreCommit,
PrePush,
}
impl HookStage {
pub fn as_str(self) -> &'static str {
match self {
Self::PreCommit => "pre-commit",
Self::PrePush => "pre-push",
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum TriggerKind {
Commit,
Push,
}
impl TriggerKind {
pub fn as_str(self) -> &'static str {
match self {
Self::Commit => "commit",
Self::Push => "push",
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum GateType {
PreCommitFramework,
Husky {
hook: HookStage,
},
Lefthook,
PlainGitHook {
hook: HookStage,
},
LintStaged,
#[allow(dead_code)]
Tooling(String),
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ChainSupport {
ManualOnly,
Unsafe,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ProposedCheck {
pub name: String,
pub triggers: Vec<TriggerKind>,
pub timeout_secs: u64,
pub source: ProposedCheckSource,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ProposedCheckSource {
PreCommit {
hook_stage: Option<String>,
config_path: Option<PathBuf>,
},
Shell {
command: String,
},
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum AdoptMode {
Inspect,
Mirror,
Chain,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn adoption_plan_default_is_empty() {
let plan = AdoptionPlan::default();
assert!(plan.findings.is_empty());
}
#[test]
fn detected_gate_clone_round_trips() {
let gate = DetectedGate {
gate_type: GateType::PreCommitFramework,
source_path: PathBuf::from(".pre-commit-config.yaml"),
proposed_checks: vec![ProposedCheck {
name: "pre-commit".to_string(),
triggers: vec![TriggerKind::Commit],
timeout_secs: 120,
source: ProposedCheckSource::PreCommit {
hook_stage: None,
config_path: None,
},
}],
chain_support: ChainSupport::ManualOnly,
manual_chain_instructions: Some("Follow the manual steps.".to_string()),
warnings: vec!["duplicate execution risk".to_string()],
};
let cloned = gate.clone();
assert_eq!(gate, cloned);
}
#[test]
fn adopt_mode_variants_are_distinct() {
assert_ne!(AdoptMode::Inspect, AdoptMode::Mirror);
assert_ne!(AdoptMode::Mirror, AdoptMode::Chain);
assert_ne!(AdoptMode::Inspect, AdoptMode::Chain);
}
#[test]
fn chain_support_copy_semantics() {
let cs = ChainSupport::ManualOnly;
let cs2 = cs;
assert_eq!(cs, cs2);
}
#[test]
fn trigger_kind_as_str() {
assert_eq!(TriggerKind::Commit.as_str(), "commit");
assert_eq!(TriggerKind::Push.as_str(), "push");
}
#[test]
fn hook_stage_as_str() {
assert_eq!(HookStage::PreCommit.as_str(), "pre-commit");
assert_eq!(HookStage::PrePush.as_str(), "pre-push");
}
}