use crate::cli::ScanCommand;
use crate::ui::theme::ThemeMap;
use crate::ui::redaction_summary;
use anyhow::{Result, Context, anyhow};
use std::io::{self, Read, Write};
use std::fs;
use is_terminal::IsTerminal;
use cleansh_core::engine::SanitizationEngine;
use cleansh_core::RedactionMatch;
use std::collections::HashMap;
pub fn run_stats_command(opts: &ScanCommand, theme_map: &ThemeMap, engine: &dyn SanitizationEngine) -> Result<()> {
let enable_colors = io::stderr().is_terminal();
let input_content = if let Some(path) = &opts.input_file {
fs::read_to_string(path)
.with_context(|| format!("Failed to read input file: {}", path.display()))?
} else {
let mut content = String::new();
io::stdin().read_to_string(&mut content)?;
content
};
let source_name = opts.input_file.clone()
.unwrap_or_default()
.display()
.to_string();
let source_name = if source_name.is_empty() {
"stdin".to_string()
} else {
source_name
};
let all_matches = engine.find_matches_for_ui(&input_content, &source_name)
.context("Failed to analyze content for statistics")?;
let mut aggregated_matches: HashMap<String, Vec<&RedactionMatch>> = HashMap::new();
for m in &all_matches {
aggregated_matches.entry(m.rule_name.clone()).or_insert_with(Vec::new).push(m);
}
if let Some(threshold) = opts.fail_over_threshold {
if all_matches.len() > threshold {
redaction_summary::print_stats_fail_over_message(
threshold,
all_matches.len(),
&mut io::stderr(),
theme_map,
enable_colors,
).ok();
return Err(anyhow!("FAIL-OVER threshold exceeded."));
}
}
#[derive(serde::Serialize)]
struct StatsSummary {
redaction_summary: HashMap<String, usize>,
}
let summary_map: HashMap<String, usize> = aggregated_matches
.iter()
.map(|(rule_name, matches)| (rule_name.clone(), matches.len()))
.collect();
let json_output = serde_json::to_string_pretty(&StatsSummary { redaction_summary: summary_map })
.context("Failed to serialize stats summary to JSON")?;
if let Some(json_path) = &opts.json_file {
fs::write(json_path, json_output.as_bytes())
.with_context(|| format!("Failed to write JSON output to file: {}", json_path.display()))?;
} else if opts.json_stdout {
io::stdout().write_all(json_output.as_bytes())
.context("Failed to write JSON output to stdout")?;
io::stdout().write_all(b"\n")
.context("Failed to write newline to stdout")?;
} else {
redaction_summary::print_summary_for_stats_mode(
&aggregated_matches,
engine.compiled_rules(),
&mut io::stderr(),
theme_map,
opts.sample_matches,
enable_colors,
).ok(); }
Ok(())
}