1use std::path::PathBuf;
4use std::process::ExitCode;
5
6use crate::cli::DebugArgs;
7use crate::config::{self, ConfigSourceInfo};
8use crate::error::RippyError;
9use crate::inspect;
10
11pub fn run(args: &DebugArgs) -> Result<ExitCode, RippyError> {
17 let cwd = std::env::current_dir().unwrap_or_else(|_| PathBuf::from("."));
18 let trace = inspect::collect_trace_data(&args.command, &cwd, args.config.as_deref())?;
19 let sources = config::enumerate_config_sources(&cwd, args.config.as_deref());
20
21 if args.json {
22 let mut json_output = serde_json::json!({
23 "command": trace.command,
24 "sources": sources,
25 "decision": trace.decision,
26 "reason": trace.reason,
27 "steps": trace.steps.iter().map(|s| serde_json::json!({
28 "stage": s.stage,
29 "matched": s.matched,
30 "detail": s.detail,
31 })).collect::<Vec<_>>(),
32 });
33 if let Some(resolved) = &trace.resolved {
34 json_output["resolved"] = serde_json::Value::String(resolved.clone());
35 }
36 let json = serde_json::to_string_pretty(&json_output)
37 .map_err(|e| RippyError::Setup(format!("JSON serialization failed: {e}")))?;
38 println!("{json}");
39 } else {
40 print_debug_text(&trace, &sources);
41 }
42
43 Ok(ExitCode::SUCCESS)
44}
45
46fn print_debug_text(trace: &inspect::TraceOutput, sources: &[ConfigSourceInfo]) {
47 println!("Command: {}", trace.command);
48 if let Some(resolved) = &trace.resolved {
49 println!("Resolved: {resolved}");
50 }
51 println!();
52
53 println!("Config sources:");
54 for (i, source) in sources.iter().enumerate() {
55 let path_info = source
56 .path
57 .as_ref()
58 .map_or(String::new(), |p| format!(" ({})", p.display()));
59 println!(" {}. {}{path_info}", i + 1, source.tier);
60 }
61
62 println!("\nDecision trace:");
63 for (i, step) in trace.steps.iter().enumerate() {
64 let status = if step.matched { "+" } else { "-" };
65 println!(" {}. {:<16} [{status}] {}", i + 1, step.stage, step.detail);
66 }
67
68 println!("\nVerdict: {}", trace.decision.to_uppercase());
69 println!(" Reason: {}", trace.reason);
70}