tcss_cli/commands/
check.rs1use anyhow::{Context, Result};
4use colored::*;
5use std::fs;
6use std::path::Path;
7use std::time::Instant;
8use tcss_core::{Lexer, Parser};
9
10use crate::output;
11
12pub fn check_files(files: &[impl AsRef<Path>], _verbose: bool) -> Result<()> {
14 let start_time = Instant::now();
15 let mut total_files = 0;
16 let mut error_count = 0;
17
18 output::header("Checking TCSS Files");
19 output::debug(&format!("Checking {} file(s)", files.len()));
20 println!();
21
22 for file in files {
23 let file = file.as_ref();
24 total_files += 1;
25
26 output::verbose(&format!("Checking: {}", file.display()));
27 output::debug(&format!("File {}/{}: {}", total_files, files.len(), file.display()));
28
29 match check_file(file, _verbose) {
30 Ok(_) => {
31 output::success(&file.display().to_string());
32 }
33 Err(e) => {
34 error_count += 1;
35 output::error(&file.display().to_string());
36 eprintln!(" {}", e.to_string().bright_red());
37 output::debug(&format!("Error details: {:?}", e));
38 }
39 }
40 }
41
42 println!();
43 output::timing("Check", start_time.elapsed().as_millis());
44
45 if error_count == 0 {
47 output::success(&format!("All {} file(s) passed!", total_files));
48 output::debug("No errors found");
49 Ok(())
50 } else {
51 println!(
52 "{} {} file(s) with errors, {} file(s) passed",
53 "✗".bright_red().bold(),
54 error_count.to_string().bright_red(),
55 (total_files - error_count).to_string().bright_green()
56 );
57 output::debug(&format!("Found {} error(s) in {} file(s)", error_count, total_files));
58 anyhow::bail!("Check failed with {} error(s)", error_count)
59 }
60}
61
62fn check_file(file: &Path, _verbose: bool) -> Result<()> {
64 let source = fs::read_to_string(file)
66 .with_context(|| format!("Failed to read file: {}", file.display()))?;
67
68 let mut lexer = Lexer::new(&source);
70 let tokens = lexer
71 .tokenize()
72 .map_err(|e| anyhow::anyhow!("Lexer error: {}", e))?;
73
74 let mut parser = Parser::new(tokens);
76 parser
77 .parse()
78 .map_err(|e| anyhow::anyhow!("Parser error: {}", e))?;
79
80 Ok(())
81}
82
83#[cfg(test)]
84mod tests {
85 use super::*;
86 use std::io::Write;
87 use tempfile::NamedTempFile;
88
89 #[test]
90 fn test_check_valid_file() {
91 let mut file = NamedTempFile::new().unwrap();
92 writeln!(
93 file,
94 r#"
95@var primary: #3498db
96
97.button:
98 background: primary
99"#
100 )
101 .unwrap();
102
103 let result = check_file(file.path(), false);
104 assert!(result.is_ok());
105 }
106
107 #[test]
108 fn test_check_invalid_file() {
109 let mut file = NamedTempFile::new().unwrap();
110 writeln!(
111 file,
112 r#"
113@var primary: #3498db
114
115.button
116 background: primary
117"#
118 )
119 .unwrap();
120
121 let result = check_file(file.path(), false);
122 assert!(result.is_err());
123 }
124}
125