ggen_cli_lib/cmds/graph/
diff.rs1use clap::Args;
2use ggen_core::delta::{GraphDelta, ImpactAnalyzer};
3use ggen_core::graph::Graph;
4use ggen_utils::error::Result;
5use std::path::PathBuf;
6
7#[derive(Args, Debug)]
8pub struct DiffArgs {
9 #[arg(short, long)]
11 pub baseline: PathBuf,
12
13 #[arg(short, long)]
15 pub current: PathBuf,
16
17 #[arg(short = 'o', long, default_value = "human")]
19 pub format: String,
20
21 #[arg(short = 'f', long)]
23 pub filter: Vec<String>,
24
25 #[arg(short = 't', long)]
27 pub show_templates: bool,
28
29 #[arg(short = 'T', long)]
31 pub template_paths: Vec<PathBuf>,
32}
33
34pub async fn run(args: &DiffArgs) -> Result<()> {
35 let baseline_graph = Graph::new()?;
37 baseline_graph.load_path(&args.baseline)?;
38
39 let current_graph = Graph::new()?;
41 current_graph.load_path(&args.current)?;
42
43 let delta = GraphDelta::new(&baseline_graph, ¤t_graph)?;
45
46 let delta = if !args.filter.is_empty() {
48 delta.filter_by_iris(&args.filter)
49 } else {
50 delta
51 };
52
53 match args.format.as_str() {
55 "human" => print_human(&delta)?,
56 "json" => println!("{}", serde_json::to_string_pretty(&delta)?),
57 "hash" => println!("{}", delta.baseline_hash.as_ref().unwrap_or(&String::new())),
58 _ => {
59 return Err(ggen_utils::error::Error::new(&format!(
60 "Unknown format: {}",
61 args.format
62 )))
63 }
64 }
65
66 if args.show_templates && !args.template_paths.is_empty() {
68 let mut analyzer = ImpactAnalyzer::new();
69 let impacts = analyzer.analyze_impacts(
70 &delta,
71 &args
72 .template_paths
73 .iter()
74 .map(|p| p.to_string_lossy().to_string())
75 .collect::<Vec<_>>(),
76 &baseline_graph,
77 )?;
78
79 println!("\nTemplate Impacts:");
80 for impact in impacts {
81 if impact.is_confident(0.1) {
82 println!(
83 " {}: {:.2} - {}",
84 impact.template_path, impact.confidence, impact.reason
85 );
86 }
87 }
88 }
89
90 Ok(())
91}
92
93fn print_human(delta: &GraphDelta) -> Result<()> {
94 if delta.is_empty() {
95 println!("No changes detected between graphs.");
96 return Ok(());
97 }
98
99 println!("Graph Delta Summary:");
100 println!(
101 " Baseline hash: {}",
102 delta.baseline_hash.as_deref().unwrap_or("unknown")
103 );
104 println!(
105 " Current hash: {}",
106 delta.current_hash.as_deref().unwrap_or("unknown")
107 );
108 println!(
109 " Computed at: {}",
110 delta.computed_at.format("%Y-%m-%d %H:%M:%S UTC")
111 );
112
113 let counts = delta.counts();
114 println!(" Changes:");
115 println!(
116 " Additions: {}",
117 counts.get("additions").unwrap_or(&0)
118 );
119 println!(
120 " Deletions: {}",
121 counts.get("deletions").unwrap_or(&0)
122 );
123 println!(
124 " Modifications: {}",
125 counts.get("modifications").unwrap_or(&0)
126 );
127
128 if !delta.deltas.is_empty() {
129 println!("\nDetailed Changes:");
130 for (i, delta_change) in delta.deltas.iter().enumerate() {
131 println!(" {}. {}", i + 1, delta_change);
132 }
133 }
134
135 Ok(())
136}