es_fluent_cli/commands/check/
mod.rs1mod inventory;
11mod validation;
12
13use crate::commands::{WorkspaceArgs, WorkspaceCrates};
14use crate::core::{CliError, ValidationIssue, ValidationReport};
15use crate::generation::{prepare_monolithic_runner_crate, run_monolithic};
16use crate::utils::ui;
17use clap::Parser;
18use std::collections::HashSet;
19
20#[derive(Debug, Parser)]
22pub struct CheckArgs {
23 #[command(flatten)]
24 pub workspace: WorkspaceArgs,
25
26 #[arg(long)]
28 pub all: bool,
29
30 #[arg(long, value_delimiter = ',')]
33 pub ignore: Vec<String>,
34
35 #[arg(long)]
37 pub force_run: bool,
38}
39
40pub fn run_check(args: CheckArgs) -> Result<(), CliError> {
42 let workspace = WorkspaceCrates::discover(args.workspace)?;
43
44 if !workspace.print_discovery(ui::print_check_header) {
45 ui::print_no_crates_found();
46 return Ok(());
47 }
48
49 let ignore_crates: HashSet<String> = args.ignore.into_iter().collect();
51 let force_run = args.force_run;
52
53 let crates_to_check: Vec<_> = workspace
55 .valid
56 .iter()
57 .filter(|k| !ignore_crates.contains(&k.name))
58 .collect();
59
60 if !ignore_crates.is_empty() {
62 let all_crate_names: HashSet<String> =
63 workspace.valid.iter().map(|k| k.name.clone()).collect();
64
65 let mut unknown_crates: Vec<&String> = ignore_crates
66 .iter()
67 .filter(|c| !all_crate_names.contains(*c))
68 .collect();
69
70 if !unknown_crates.is_empty() {
71 unknown_crates.sort();
73
74 return Err(CliError::Other(format!(
75 "Unknown crates passed to --ignore: {}",
76 unknown_crates
77 .iter()
78 .map(|c| format!("'{}'", c))
79 .collect::<Vec<_>>()
80 .join(", ")
81 )));
82 }
83 }
84
85 if crates_to_check.is_empty() {
86 ui::print_no_crates_found();
87 return Ok(());
88 }
89
90 prepare_monolithic_runner_crate(&workspace.workspace_info)
92 .map_err(|e| CliError::Other(e.to_string()))?;
93
94 let temp_dir =
96 es_fluent_derive_core::get_es_fluent_temp_dir(&workspace.workspace_info.root_dir);
97
98 let pb = ui::create_progress_bar(crates_to_check.len() as u64, "Collecting keys...");
99
100 for krate in &crates_to_check {
101 pb.set_message(format!("Scanning {}", krate.name));
102 run_monolithic(
103 &workspace.workspace_info,
104 "check",
105 &krate.name,
106 &[],
107 force_run,
108 )
109 .map_err(|e| CliError::Other(e.to_string()))?;
110 pb.inc(1);
111 }
112
113 pb.finish_and_clear();
114
115 let mut all_issues: Vec<ValidationIssue> = Vec::new();
117
118 let pb = ui::create_progress_bar(crates_to_check.len() as u64, "Checking crates...");
119
120 for krate in &crates_to_check {
121 pb.set_message(format!("Checking {}", krate.name));
122
123 match validation::validate_crate(
124 krate,
125 &workspace.workspace_info.root_dir,
126 &temp_dir,
127 args.all,
128 ) {
129 Ok(issues) => {
130 all_issues.extend(issues);
131 },
132 Err(e) => {
133 pb.suspend(|| {
135 ui::print_check_error(&krate.name, &e.to_string());
136 });
137 },
138 }
139 pb.inc(1);
140 }
141
142 pb.finish_and_clear();
143
144 all_issues.sort_by_cached_key(|issue| issue.sort_key());
146
147 let error_count = all_issues
148 .iter()
149 .filter(|i| {
150 matches!(
151 i,
152 ValidationIssue::MissingKey(_) | ValidationIssue::SyntaxError(_)
153 )
154 })
155 .count();
156 let warning_count = all_issues
157 .iter()
158 .filter(|i| matches!(i, ValidationIssue::MissingVariable(_)))
159 .count();
160
161 if all_issues.is_empty() {
162 ui::print_check_success();
163 Ok(())
164 } else {
165 Err(CliError::Validation(ValidationReport {
166 error_count,
167 warning_count,
168 issues: all_issues,
169 }))
170 }
171}