Skip to main content

sakurs_cli/commands/
validate.rs

1//! Validate command implementation
2
3use anyhow::Result;
4use clap::Args;
5use std::path::PathBuf;
6
7/// Arguments for the validate command
8#[derive(Debug, Args)]
9pub struct ValidateArgs {
10    /// Path to language configuration file to validate
11    #[arg(short = 'c', long, value_name = "FILE", required = true)]
12    pub language_config: PathBuf,
13}
14
15impl ValidateArgs {
16    /// Execute the validate command
17    pub fn execute(&self) -> Result<()> {
18        use sakurs_core::{Config, LanguageConfig, SentenceProcessor};
19
20        println!(
21            "Validating language configuration: {}",
22            self.language_config.display()
23        );
24
25        // Load and schema-validate the configuration, then compile it the
26        // same way processing would (this catches rule-level problems such
27        // as invalid regexes or rules exceeding the judgment window).
28        let load = || -> std::result::Result<LanguageConfig, String> {
29            let config = LanguageConfig::from_file(&self.language_config, None)
30                .map_err(|e| e.to_string())?;
31            SentenceProcessor::with_language_config(Config::default(), &config)
32                .map_err(|e| e.to_string())?;
33            Ok(config)
34        };
35
36        match load() {
37            Ok(config) => {
38                println!("✓ Configuration is valid!");
39                println!("  Language code: {}", config.metadata.code);
40                println!("  Language name: {}", config.metadata.name);
41                Ok(())
42            }
43            Err(e) => {
44                println!("✗ Configuration is invalid!");
45                println!("  Error: {e}");
46                Err(anyhow::anyhow!("Validation failed: {}", e))
47            }
48        }
49    }
50}
51
52#[cfg(test)]
53mod tests {
54    use super::*;
55    use std::io::Write;
56    use tempfile::NamedTempFile;
57
58    #[test]
59    fn test_validate_args_debug() {
60        let args = ValidateArgs {
61            language_config: PathBuf::from("test.toml"),
62        };
63
64        let debug_str = format!("{:?}", args);
65        assert!(debug_str.contains("ValidateArgs"));
66        assert!(debug_str.contains("test.toml"));
67    }
68
69    #[test]
70    fn test_validate_valid_config() {
71        let toml_content = r#"
72[metadata]
73code = "test"
74name = "Test Language"
75
76[terminators]
77chars = ["."]
78
79[ellipsis]
80patterns = []
81
82[enclosures]
83pairs = []
84
85[suppression]
86
87[abbreviations]
88
89[sentence_starters]
90common = ["The", "A"]
91"#;
92
93        let mut temp_file = NamedTempFile::new().unwrap();
94        write!(temp_file, "{}", toml_content).unwrap();
95
96        let args = ValidateArgs {
97            language_config: temp_file.path().to_path_buf(),
98        };
99
100        assert!(args.execute().is_ok());
101    }
102
103    #[test]
104    fn test_validate_invalid_config() {
105        let toml_content = r#"
106[metadata]
107code = ""
108name = "Test"
109
110[terminators]
111chars = ["."]
112"#;
113
114        let mut temp_file = NamedTempFile::new().unwrap();
115        write!(temp_file, "{}", toml_content).unwrap();
116
117        let args = ValidateArgs {
118            language_config: temp_file.path().to_path_buf(),
119        };
120
121        assert!(args.execute().is_err());
122    }
123}