Skip to main content

ferrous_forge/commands/validate/
mod.rs

1//! Validate command implementation
2
3/// AI-friendly compliance report generation.
4pub mod ai_report;
5/// Validation check execution and result collection.
6pub mod checks;
7/// Markdown report formatting for validation results.
8pub mod markdown;
9/// Shared utilities for the validate command.
10pub mod utils;
11
12use ai_report::generate_ai_report;
13use checks::{run_additional_checks, run_clippy_validation};
14
15use crate::{
16    Result,
17    config::Config,
18    validation::{RustValidator, Violation, ViolationType},
19};
20use console::style;
21use std::path::{Path, PathBuf};
22
23/// Execute the validate command
24///
25/// # Errors
26///
27/// Returns an error if the configuration cannot be loaded, the validator
28/// fails to initialize, or the validation process encounters an I/O error.
29pub async fn execute(path: Option<PathBuf>, ai_report: bool, locked_only: bool) -> Result<()> {
30    let project_path = path.unwrap_or_else(|| std::env::current_dir().unwrap_or_default());
31
32    print_header(&project_path);
33
34    // Load config so validators use project-configured limits and locked settings
35    let config = Config::load_or_default().await?;
36    let validator = RustValidator::with_config(project_path.clone(), config)?;
37    let violations = validator.validate_project().await?;
38
39    if locked_only {
40        return handle_locked_only_check(&violations);
41    }
42
43    display_validation_results(&validator, &violations)?;
44
45    if ai_report {
46        generate_ai_report_with_message(&project_path, &violations).await?;
47    }
48
49    let clippy_result = run_clippy_validation(&validator).await?;
50    run_additional_checks(&project_path).await;
51
52    handle_final_result(&violations, &clippy_result);
53
54    Ok(())
55}
56
57fn print_header(project_path: &Path) {
58    println!();
59    println!("{}", style("šŸ¦€ Running Ferrous Forge validation...").bold());
60    println!();
61
62    println!("šŸ“ Project: {}", project_path.display());
63    println!();
64}
65
66fn display_validation_results(validator: &RustValidator, violations: &[Violation]) -> Result<()> {
67    let report = validator.generate_report(violations);
68    println!("{}", report);
69    Ok(())
70}
71
72/// When --locked-only is set, only report and fail on locked setting violations
73fn handle_locked_only_check(violations: &[Violation]) -> Result<()> {
74    let locked: Vec<&Violation> = violations
75        .iter()
76        .filter(|v| {
77            matches!(
78                v.violation_type,
79                ViolationType::WrongEdition
80                    | ViolationType::OldRustVersion
81                    | ViolationType::LockedSetting
82            )
83        })
84        .collect();
85
86    if locked.is_empty() {
87        println!("{}", style("āœ… No locked setting violations.").green());
88        return Ok(());
89    }
90
91    eprintln!("\nāŒ FERROUS FORGE — Locked Setting Violations\n");
92    for v in &locked {
93        eprintln!("{}\n", v.message);
94    }
95    std::process::exit(1);
96}
97
98async fn generate_ai_report_with_message(
99    project_path: &PathBuf,
100    violations: &[Violation],
101) -> Result<()> {
102    generate_ai_report(project_path, violations).await
103}
104
105fn handle_final_result(violations: &[Violation], clippy_result: &crate::validation::ClippyResult) {
106    if !violations.is_empty() || !clippy_result.success {
107        println!(
108            "{}",
109            style("āŒ Validation completed with issues").red().bold()
110        );
111        std::process::exit(1);
112    } else {
113        println!(
114            "{}",
115            style("āœ… All validation checks passed!").green().bold()
116        );
117    }
118}