the_code_graph_domain/analysis/
blast_radius.rs1use crate::model::*;
2use crate::traversal::InMemoryGraph;
3
4pub fn compute_blast_radius(
8 graph: &InMemoryGraph,
9 targets: &[ImpactTarget],
10 max_depth: usize,
11 min_confidence: Confidence,
12) -> ImpactReport {
13 let mut all_affected = Vec::new();
14
15 for target in targets {
16 let start = match target {
17 ImpactTarget::Symbol(s) => s.as_str(),
18 ImpactTarget::File(p) => p.to_str().unwrap_or_default(),
19 };
20 let results = graph.bfs_filtered(start, Direction::Forward, max_depth, min_confidence);
21 for r in results {
22 all_affected.push(AffectedNode {
23 qualified_name: r.node,
24 depth: r.depth,
25 confidence: r.edge_kind.confidence(),
26 path: r.path,
27 });
28 }
29 }
30
31 ImpactReport {
32 targets: targets.to_vec(),
33 affected: all_affected,
34 depth: max_depth,
35 min_confidence,
36 }
37}
38
39#[cfg(test)]
40mod tests {
41 use super::*;
42 use crate::traversal::InMemoryGraph;
43
44 #[test]
45 fn blast_radius_from_single_symbol() {
46 let edges = vec![
47 Edge {
48 kind: EdgeKind::Calls,
49 source: "a::foo".into(),
50 target: "b::bar".into(),
51 metadata: None,
52 },
53 Edge {
54 kind: EdgeKind::Calls,
55 source: "b::bar".into(),
56 target: "c::baz".into(),
57 metadata: None,
58 },
59 ];
60 let graph = InMemoryGraph::from_edges(edges);
61 let targets = vec![ImpactTarget::Symbol("a::foo".into())];
62 let report = compute_blast_radius(&graph, &targets, 3, Confidence::Structural);
63 assert!(!report.affected.is_empty());
64 assert!(report.affected.iter().any(|n| n.qualified_name == "b::bar"));
65 assert!(report.affected.iter().any(|n| n.qualified_name == "c::baz"));
66 }
67
68 #[test]
69 fn blast_radius_from_file_target() {
70 let edges = vec![
71 Edge {
72 kind: EdgeKind::Contains,
73 source: "a.rs".into(),
74 target: "a.rs::foo".into(),
75 metadata: None,
76 },
77 Edge {
78 kind: EdgeKind::Calls,
79 source: "a.rs::foo".into(),
80 target: "b.rs::bar".into(),
81 metadata: None,
82 },
83 ];
84 let graph = InMemoryGraph::from_edges(edges);
85 let targets = vec![ImpactTarget::File("a.rs".into())];
86 let report = compute_blast_radius(&graph, &targets, 3, Confidence::Structural);
87 assert!(!report.targets.is_empty());
88 }
89}