use anyhow::Result;
use crate::cache::CacheManager;
pub(super) fn handle_snapshot_create() -> Result<()> {
let cache = CacheManager::new(".");
if !cache.path().exists() {
anyhow::bail!("No .reflex cache found. Run `rfx index` first.");
}
let info = crate::pulse::snapshot::create_snapshot(&cache)?;
eprintln!("Snapshot created: {}", info.id);
eprintln!(" Files: {}, Lines: {}, Edges: {}", info.file_count, info.total_lines, info.edge_count);
if let Some(branch) = &info.git_branch {
eprintln!(" Branch: {}", branch);
}
let pulse_config = crate::pulse::config::load_pulse_config(cache.path())?;
let gc_report = crate::pulse::snapshot::run_gc(&cache, &pulse_config.retention)?;
if gc_report.removed > 0 {
eprintln!(" GC: removed {} old snapshot(s)", gc_report.removed);
}
Ok(())
}
pub(super) fn handle_snapshot_list(json: bool, pretty: bool) -> Result<()> {
let cache = CacheManager::new(".");
if !cache.path().exists() {
anyhow::bail!("No .reflex cache found. Run `rfx index` first.");
}
let snapshots = crate::pulse::snapshot::list_snapshots(&cache)?;
if json || pretty {
let output = if pretty {
serde_json::to_string_pretty(&snapshots)?
} else {
serde_json::to_string(&snapshots)?
};
println!("{}", output);
} else {
if snapshots.is_empty() {
eprintln!("No snapshots found. Run `rfx snapshot` to create one.");
return Ok(());
}
println!("{:<20} {:>6} {:>8} {:>6} {}", "ID", "Files", "Lines", "Edges", "Branch");
println!("{}", "-".repeat(60));
for s in &snapshots {
println!("{:<20} {:>6} {:>8} {:>6} {}",
s.id, s.file_count, s.total_lines, s.edge_count,
s.git_branch.as_deref().unwrap_or("-"));
}
eprintln!("\n{} snapshot(s)", snapshots.len());
}
Ok(())
}
pub(super) fn handle_snapshot_diff(
baseline: Option<String>,
current: Option<String>,
json: bool,
pretty: bool,
) -> Result<()> {
let cache = CacheManager::new(".");
if !cache.path().exists() {
anyhow::bail!("No .reflex cache found. Run `rfx index` first.");
}
let snapshots = crate::pulse::snapshot::list_snapshots(&cache)?;
let pulse_config = crate::pulse::config::load_pulse_config(cache.path())?;
let current_snapshot = match ¤t {
Some(id) => snapshots.iter().find(|s| s.id == *id)
.ok_or_else(|| anyhow::anyhow!("Snapshot '{}' not found", id))?,
None => snapshots.first()
.ok_or_else(|| anyhow::anyhow!("No snapshots found. Run `rfx snapshot` first."))?,
};
let baseline_snapshot = match &baseline {
Some(id) => snapshots.iter().find(|s| s.id == *id)
.ok_or_else(|| anyhow::anyhow!("Snapshot '{}' not found", id))?,
None => snapshots.get(1)
.ok_or_else(|| anyhow::anyhow!("Need at least 2 snapshots to diff. Run `rfx snapshot` again after making changes."))?,
};
let diff = crate::pulse::diff::compute_diff(
&baseline_snapshot.path,
¤t_snapshot.path,
&pulse_config.thresholds,
)?;
if json || pretty {
let output = if pretty {
serde_json::to_string_pretty(&diff)?
} else {
serde_json::to_string(&diff)?
};
println!("{}", output);
} else {
let s = &diff.summary;
println!("Diff: {} → {}", diff.baseline_id, diff.current_id);
println!(" Files: +{} -{} ~{}", s.files_added, s.files_removed, s.files_modified);
println!(" Edges: +{} -{}", s.edges_added, s.edges_removed);
if !diff.threshold_alerts.is_empty() {
println!(" Alerts: {}", diff.threshold_alerts.len());
for alert in &diff.threshold_alerts {
println!(" [{:?}] {}", alert.severity, alert.message);
}
}
}
Ok(())
}
pub(super) fn handle_snapshot_gc(json: bool) -> Result<()> {
let cache = CacheManager::new(".");
if !cache.path().exists() {
anyhow::bail!("No .reflex cache found. Run `rfx index` first.");
}
let pulse_config = crate::pulse::config::load_pulse_config(cache.path())?;
let report = crate::pulse::snapshot::run_gc(&cache, &pulse_config.retention)?;
if json {
println!("{}", serde_json::to_string(&report)?);
} else {
println!("GC complete: before {}, after {}, removed {}", report.snapshots_before, report.snapshots_after, report.removed);
}
Ok(())
}