acp/commands/
validate.rs

1//! @acp:module "Validate Command"
2//! @acp:summary "Validate cache/vars files against schemas"
3//! @acp:domain cli
4//! @acp:layer handler
5
6use std::path::PathBuf;
7
8use anyhow::Result;
9use console::style;
10
11use crate::schema;
12
13/// Options for the validate command
14#[derive(Debug, Clone)]
15pub struct ValidateOptions {
16    /// File to validate
17    pub file: PathBuf,
18}
19
20/// Execute the validate command
21pub fn execute_validate(options: ValidateOptions) -> Result<()> {
22    let content = std::fs::read_to_string(&options.file)?;
23    let filename = options.file.to_string_lossy();
24
25    // Use detect_schema_type() for all 6 schema types
26    if let Some(schema_type) = schema::detect_schema_type(&filename) {
27        schema::validate_by_type(&content, schema_type)?;
28        println!(
29            "{} {} file is valid",
30            style("✓").green(),
31            schema_type.to_uppercase()
32        );
33    } else {
34        // Try auto-detection from $schema field
35        let json: serde_json::Value = match serde_json::from_str(&content) {
36            Ok(json) => json,
37            Err(e) => {
38                eprintln!("{} File is not valid JSON: {}", style("✗").red(), e);
39                eprintln!();
40                eprintln!("The validate command validates ACP JSON files:");
41                eprintln!("  - .acp/acp.cache.json  (cache)");
42                eprintln!("  - .acp/acp.vars.json   (vars)");
43                eprintln!("  - .acp.config.json     (config)");
44                eprintln!("  - .acp/acp.attempts.json (attempts)");
45                eprintln!("  - sync files           (sync)");
46                eprintln!("  - primer files         (primer)");
47                eprintln!();
48                eprintln!("For source code validation, use: acp annotate --check");
49                std::process::exit(1);
50            }
51        };
52        if let Some(schema_url) = json.get("$schema").and_then(|s| s.as_str()) {
53            let detected = if schema_url.contains("cache") {
54                "cache"
55            } else if schema_url.contains("vars") {
56                "vars"
57            } else if schema_url.contains("config") {
58                "config"
59            } else if schema_url.contains("attempts") {
60                "attempts"
61            } else if schema_url.contains("sync") {
62                "sync"
63            } else if schema_url.contains("primer") {
64                "primer"
65            } else {
66                ""
67            };
68
69            if !detected.is_empty() {
70                schema::validate_by_type(&content, detected)?;
71                println!(
72                    "{} {} file is valid",
73                    style("✓").green(),
74                    detected.to_uppercase()
75                );
76            } else {
77                eprintln!(
78                    "{} Unknown schema type. Could not detect from filename or $schema field.",
79                    style("✗").red()
80                );
81                std::process::exit(1);
82            }
83        } else {
84            eprintln!(
85                "{} Unknown file type. Provide filename with schema type (cache, vars, config, primer, attempts, sync) or include $schema field.",
86                style("✗").red()
87            );
88            std::process::exit(1);
89        }
90    }
91
92    Ok(())
93}