use crate::core::Severity;
use crate::importer::{self, ExternalTool, ImportResult};
use super::ImportArgs;
use anyhow::Result;
use colored::Colorize;
use std::path::PathBuf;
pub fn run(args: &ImportArgs) -> Result<()> {
let tool = ExternalTool::parse(&args.from)?;
let input_path = args
.input
.clone()
.unwrap_or_else(|| PathBuf::from("results.json"));
if !input_path.exists() {
anyhow::bail!("Results file not found: {}", input_path.display());
}
let content = std::fs::read_to_string(&input_path)?;
let imported = importer::import_from_json(tool, &content)?;
let forge_guard_findings = match &args.findings {
Some(path) if path.exists() => importer::load_forge_guard_findings(path)?,
Some(path) => {
anyhow::bail!("Forge-guard findings file not found: {}", path.display());
}
None => Vec::new(),
};
let result = if args.findings.is_some() {
importer::build_unified(tool, forge_guard_findings, imported)
} else {
let (deduped, removed) = importer::deduplicate(&[], imported);
let mut files: Vec<String> = deduped.iter().filter_map(|f| f.file.clone()).collect();
files.sort();
files.dedup();
ImportResult {
tool,
findings: deduped,
duplicates_removed: removed,
source_files: files,
merged_with_forge_guard: false,
}
};
if args.shared.json || args.shared.markdown {
if let Some(out) = &args.output {
let json = serde_json::to_string_pretty(&result)?;
std::fs::write(out, json)?;
eprintln!("📄 Unified report written to {}", out.display());
} else if args.shared.json {
println!("{}", serde_json::to_string_pretty(&result)?);
} else {
println!("{}", render_markdown(&result));
}
} else if let Some(out) = &args.output {
let json = serde_json::to_string_pretty(&result)?;
std::fs::write(out, json)?;
eprintln!("📄 Unified report written to {}", out.display());
} else {
print_terminal(&result);
}
Ok(())
}
fn print_terminal(result: &ImportResult) {
println!(
"\n{}\n",
"═══════════════════════════════════════════════════".bright_blue()
);
println!(
"{} {}\n",
"EXTERNAL ANALYZER IMPORT —".bright_blue().bold(),
result.tool.as_str().to_uppercase().bright_blue().bold()
);
println!(
"{}\n",
"═══════════════════════════════════════════════════".bright_blue()
);
println!(" 🔧 Source: {}", result.tool.as_str().cyan());
println!(" 📄 Files: {}", result.source_files.len());
println!(
" 🧹 Duplicates: {} removed",
result.duplicates_removed.to_string().yellow()
);
println!(
" 🛡️ Findings: {} total",
result.total().to_string().bold()
);
if result.merged_with_forge_guard {
println!(" 🔗 Merged with forge-guard findings: yes");
}
let critical = result.count_at_or_above(Severity::Critical);
let high = result
.findings
.iter()
.filter(|f| f.severity == Severity::High)
.count();
let medium = result
.findings
.iter()
.filter(|f| f.severity == Severity::Medium)
.count();
let low = result
.findings
.iter()
.filter(|f| f.severity == Severity::Low || f.severity == Severity::Informational)
.count();
println!(
" 🛑 Critical: {} 🔴 High: {} 🟡 Medium: {} 🔵 Low/Info: {}",
critical, high, medium, low
);
if !result.findings.is_empty() {
println!("\n{}", "── Findings ──".bold());
for f in result.findings.iter().take(20) {
let location = match (&f.file, f.line) {
(Some(file), Some(line)) => format!("{}:{}", file, line),
(Some(file), None) => file.clone(),
(None, _) => "unknown".into(),
};
println!(
"\n [{}] {}",
f.severity.to_string().red().bold(),
f.title.bold()
);
println!(" 📄 {} ({})", location, f.category);
if !f.description.is_empty() {
println!(" 📝 {}", f.description);
}
if !f.recommendation.is_empty() {
println!(" 💡 {}", f.recommendation);
}
}
if result.total() > 20 {
println!("\n ... and {} more findings", result.total() - 20);
}
}
println!(
"\n{}\n",
"═══════════════════════════════════════════════════".bright_blue()
);
}
fn render_markdown(result: &ImportResult) -> String {
let mut md = String::new();
md.push_str(&format!(
"# Imported findings — {}\n\n",
result.tool.as_str()
));
md.push_str(&format!("- **Total findings:** {}\n", result.total()));
md.push_str(&format!(
"- **Duplicates removed:** {}\n",
result.duplicates_removed
));
md.push_str(&format!(
"- **Files:** {}\n\n",
result.source_files.join(", ")
));
for f in &result.findings {
md.push_str(&format!("### [{}] {}\n\n", f.severity, f.title));
md.push_str(&format!(
"- **Location:** {}:{} ({})\n",
f.file.as_deref().unwrap_or("unknown"),
f.line.map_or_else(|| "?".into(), |l| l.to_string()),
f.category
));
if !f.description.is_empty() {
md.push_str(&format!("- **Description:** {}\n", f.description));
}
if !f.recommendation.is_empty() {
md.push_str(&format!("- **Recommendation:** {}\n", f.recommendation));
}
md.push('\n');
}
md
}