1use ascii_dag::cycles::generic::roots::{RootFindable, find_leaves_fn, find_roots_fn};
2use ascii_dag::graph::DAG;
3use ascii_dag::layout::generic::impact::{
4 ImpactAnalyzable, compute_blast_radius_fn, compute_descendants_fn,
5};
6use ascii_dag::layout::generic::metrics::GraphMetrics;
7use std::collections::HashMap;
8
9fn main() {
10 println!("=== Dependency Analysis Tool ===\n");
11
12 example_simple_analysis();
13 example_trait_based_registry();
14 example_metrics_dashboard();
15}
16
17fn example_simple_analysis() {
18 println!("1. Simple Dependency Analysis");
19 println!(" Analyzing a package dependency graph\n");
20
21 let get_deps = |pkg: &&str| match *pkg {
23 "app" => vec!["core", "ui"],
24 "ui" => vec!["core", "renderer"],
25 "renderer" => vec!["core"],
26 "core" => vec!["utils"],
27 "utils" => vec![],
28 _ => vec![],
29 };
30
31 let packages = ["app", "ui", "renderer", "core", "utils"];
32
33 let roots = find_roots_fn(&packages, get_deps);
35 println!(" 📦 Root packages (can be built first):");
36 for root in &roots {
37 println!(" - {}", root);
38 }
39 println!();
40
41 let leaves = find_leaves_fn(&packages, get_deps);
43 println!(" 🍃 Leaf packages (final outputs):");
44 for leaf in &leaves {
45 println!(" - {}", leaf);
46 }
47 println!();
48
49 let impacted = compute_descendants_fn(&packages, &"core", get_deps);
51 println!(" ⚠️ If 'core' changes, these packages need rebuilding:");
52 for pkg in &impacted {
53 println!(" - {}", pkg);
54 }
55 println!(" Impact: {} packages affected\n", impacted.len());
56
57 let (deps, impacts) = compute_blast_radius_fn(&packages, &"ui", get_deps);
59 println!(" 💥 Blast radius for 'ui':");
60 println!(" Dependencies: {:?}", deps);
61 println!(" Impacts: {:?}", impacts);
62 println!();
63}
64
65fn example_trait_based_registry() {
66 println!("2. Trait-Based Error Registry");
67 println!(" Using traits for cleaner API\n");
68
69 #[derive(Debug, Clone)]
70 struct ErrorDef {
71 id: String,
72 message: String,
73 caused_by: Vec<String>,
74 }
75
76 struct ErrorRegistry {
77 errors: HashMap<String, ErrorDef>,
78 }
79
80 impl RootFindable for ErrorRegistry {
81 type Id = String;
82
83 fn get_all_ids(&self) -> Vec<String> {
84 self.errors.keys().cloned().collect()
85 }
86
87 fn get_dependencies(&self, id: &String) -> Vec<String> {
88 self.errors
89 .get(id)
90 .map(|e| e.caused_by.clone())
91 .unwrap_or_default()
92 }
93 }
94
95 impl ImpactAnalyzable for ErrorRegistry {
96 type Id = String;
97
98 fn get_all_ids(&self) -> Vec<String> {
99 self.errors.keys().cloned().collect()
100 }
101
102 fn get_dependencies(&self, id: &String) -> Vec<String> {
103 self.errors
104 .get(id)
105 .map(|e| e.caused_by.clone())
106 .unwrap_or_default()
107 }
108 }
109
110 let mut errors = HashMap::new();
112 errors.insert(
113 "E001".to_string(),
114 ErrorDef {
115 id: "E001".to_string(),
116 message: "Invalid configuration".to_string(),
117 caused_by: vec![],
118 },
119 );
120 errors.insert(
121 "E002".to_string(),
122 ErrorDef {
123 id: "E002".to_string(),
124 message: "Database connection failed".to_string(),
125 caused_by: vec!["E001".to_string()],
126 },
127 );
128 errors.insert(
129 "E003".to_string(),
130 ErrorDef {
131 id: "E003".to_string(),
132 message: "Transaction rollback".to_string(),
133 caused_by: vec!["E002".to_string()],
134 },
135 );
136
137 let registry = ErrorRegistry { errors };
138
139 println!(" 🔍 Root errors (primary causes):");
141 for root in registry.find_roots() {
142 println!(" - {}", root);
143 }
144 println!();
145
146 println!(" 📊 Error E001 analysis:");
147 let impacted = registry.compute_descendants(&"E001".to_string());
148 println!(" Cascading errors: {}", impacted.len());
149 for err in &impacted {
150 println!(" - {}", err);
151 }
152 println!();
153}
154
155fn example_metrics_dashboard() {
156 println!("3. Dependency Graph Metrics");
157 println!(" Statistical analysis of the dependency structure\n");
158
159 let get_deps = |file: &&str| match *file {
161 "app.exe" => vec!["main.o", "utils.o", "io.o"],
162 "main.o" => vec!["main.c", "types.h"],
163 "utils.o" => vec!["utils.c", "types.h"],
164 "io.o" => vec!["io.c", "types.h"],
165 "main.c" => vec![],
166 "utils.c" => vec![],
167 "io.c" => vec![],
168 "types.h" => vec![],
169 _ => vec![],
170 };
171
172 let files = [
173 "app.exe", "main.o", "utils.o", "io.o", "main.c", "utils.c", "io.c", "types.h",
174 ];
175
176 let metrics = GraphMetrics::compute(&files, get_deps);
177
178 println!(" 📈 Graph Statistics:");
179 println!(" Total files: {}", metrics.node_count());
180 println!(" Total dependencies: {}", metrics.edge_count());
181 println!(" Root files (sources): {}", metrics.root_count());
182 println!(" Leaf files (outputs): {}", metrics.leaf_count());
183 println!(" Max depth: {}", metrics.max_depth());
184 println!(" Max impact: {} files", metrics.max_descendants());
185 println!(" Avg dependencies: {:.2}", metrics.avg_dependencies());
186 println!(" Density: {:.2}%", metrics.density() * 100.0);
187 println!();
188
189 println!(" 🔍 Graph Properties:");
190 println!(" Is tree: {}", metrics.is_tree());
191 println!(" Is forest: {}", metrics.is_forest());
192 println!(" Is sparse: {}", metrics.is_sparse());
193 println!(" Is dense: {}", metrics.is_dense());
194 println!();
195
196 println!(" 📊 Visualization:");
198 let mut dag = DAG::new();
199 dag.add_node(1, "types.h");
200 dag.add_node(2, "main.c");
201 dag.add_node(3, "main.o");
202 dag.add_node(4, "app.exe");
203 dag.add_edge(2, 3);
204 dag.add_edge(1, 3);
205 dag.add_edge(3, 4);
206
207 println!("{}", dag.render());
208
209 let mut max_impact = 0;
211 let mut most_impactful = "";
212 for file in &files {
213 let impact = compute_descendants_fn(&files, file, get_deps).len();
214 if impact > max_impact {
215 max_impact = impact;
216 most_impactful = file;
217 }
218 }
219
220 println!(
221 " ⚡ Most impactful file: '{}' (affects {} other files)",
222 most_impactful, max_impact
223 );
224 println!();
225}