use std::path::Path;
use anyhow::Result;
use crate::core::detection::scanner::{ScanSnapshotStore, Scanner};
use chrono::{TimeZone, Utc};
pub fn execute(path: &Path, tag: Option<&str>, verbose: bool, project_root: &Path) -> Result<()> {
let path = if path.exists() && path.is_relative() {
std::env::current_dir()?.join(path)
} else {
path.to_path_buf()
};
println!("Scanning {}...", path.display());
let mut scanner = Scanner::new(path.clone());
let result = scanner.scan();
result.print_summary(tag, verbose);
let snapshot = result.to_snapshot(&path);
let store = ScanSnapshotStore::new(project_root);
store.save(&snapshot)?;
println!(
"{} Scan snapshot saved: {}",
crate::utils::terminal::success_prefix(),
snapshot.id
);
match crate::core::fingerprint::ProjectFingerprint::generate(project_root) {
Ok(fp) => {
if let Err(e) = fp.save(project_root) {
eprintln!("Warning: Failed to save project fingerprint: {}", e);
}
}
Err(e) => {
eprintln!("Warning: Failed to generate project fingerprint: {}", e);
}
}
Ok(())
}
pub fn execute_list(project_root: &Path) -> Result<()> {
let store = ScanSnapshotStore::new(project_root);
let list = store.list()?;
println!();
println!("{}", crate::utils::terminal::label("Scan Snapshots:"));
println!("{}", crate::utils::terminal::label("─".repeat(50).as_str()));
if list.is_empty() {
println!(" No scan snapshots stored yet.");
return Ok(());
}
for snap in list {
let dt = Utc.timestamp_opt(snap.timestamp as i64, 0).unwrap();
let datetime = dt.format("%Y-%m-%d %H:%M:%S UTC").to_string();
println!(
" {} {} [files: {} scanned, {} skipped] [{}]",
crate::utils::terminal::bullet(),
crate::utils::terminal::label(&snap.id),
snap.file_counts.scanned,
snap.file_counts.skipped,
datetime
);
println!(" Path: {}", snap.target_path.display());
println!(" Frameworks: {}", snap.detected_frameworks.join(", "));
println!();
}
Ok(())
}
pub fn execute_show(id: &str, project_root: &Path) -> Result<()> {
let store = ScanSnapshotStore::new(project_root);
let snapshot = store.load(id)?;
let Some(snap) = snapshot else {
anyhow::bail!("Scan snapshot not found: {}", id);
};
let dt = Utc.timestamp_opt(snap.timestamp as i64, 0).unwrap();
let datetime = dt.format("%Y-%m-%d %H:%M:%S UTC").to_string();
println!();
println!("{} Scan Snapshot Detail: {}", crate::utils::terminal::success_prefix(), crate::utils::terminal::label(&snap.id));
println!(" Timestamp: {}", datetime);
println!(" Target Path: {}", snap.target_path.display());
println!();
println!(" {}", crate::utils::terminal::label("File Metrics:"));
println!(" Total Files: {}", snap.file_counts.total);
println!(" Scanned Files: {}", snap.file_counts.scanned);
println!(" Skipped Files: {}", snap.file_counts.skipped);
println!();
println!(" {}", crate::utils::terminal::label("Risk Assessment:"));
println!(" Risks Found: {}", snap.risk_counts.total);
println!();
println!(" {}", crate::utils::terminal::label("Detected Frameworks:"));
if snap.detected_frameworks.is_empty() {
println!(" None");
} else {
for fw in &snap.detected_frameworks {
println!(" - {}", fw);
}
}
println!();
println!(" {}", crate::utils::terminal::label("Recommended Recipes:"));
if snap.recipe_suggestions.is_empty() {
println!(" None");
} else {
for rec in &snap.recipe_suggestions {
println!(" - {}", rec);
}
}
Ok(())
}