cadi_core/ghost/
analyzer.rs1use crate::graph::{GraphStore, EdgeType};
2use std::collections::HashSet;
3
4pub struct DependencyAnalyzer<'a> {
6 graph: &'a GraphStore,
7}
8
9#[derive(Debug)]
10pub struct DependencyInfo {
11 pub atom_id: String,
12 pub dependencies: Vec<DependencyEdge>,
13 pub token_estimate: usize,
14}
15
16#[derive(Debug)]
17pub struct DependencyEdge {
18 pub target: String,
19 pub edge_type: EdgeType,
20 pub priority: DependencyPriority,
21}
22
23#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
24pub enum DependencyPriority {
25 Critical = 0, High = 1, Medium = 2, Low = 3, }
30
31impl<'a> DependencyAnalyzer<'a> {
32 pub fn new(graph: &'a GraphStore) -> Self {
33 Self { graph }
34 }
35
36 pub fn analyze_dependencies(
38 &self,
39 atom_ids: &[String],
40 ) -> Result<Vec<DependencyInfo>, Box<dyn std::error::Error + Send + Sync>> {
41 let mut results = Vec::new();
42
43 for atom_id in atom_ids {
44 let deps = self.graph.get_dependencies(atom_id)?;
45 let token_estimate = self.graph.get_token_estimate(atom_id)?;
46
47 let dependency_edges = deps
48 .into_iter()
49 .map(|(edge_type, target)| DependencyEdge {
50 target,
51 edge_type,
52 priority: self.calculate_priority(&edge_type),
53 })
54 .collect();
55
56 results.push(DependencyInfo {
57 atom_id: atom_id.clone(),
58 dependencies: dependency_edges,
59 token_estimate,
60 });
61 }
62
63 Ok(results)
64 }
65
66 pub fn simulate_expansion(
68 &self,
69 atom_ids: &[String],
70 policy: &super::policy::ExpansionPolicy,
71 ) -> Result<ExpansionSimulation, Box<dyn std::error::Error + Send + Sync>> {
72 let mut included = HashSet::new();
73 let mut total_tokens = 0;
74 let mut depth_reached = 0;
75
76 let mut frontier: Vec<(String, usize)> = atom_ids.iter()
78 .map(|id| (id.clone(), 0))
79 .collect();
80
81 for (atom_id, _depth) in &frontier {
82 included.insert(atom_id.clone());
83 total_tokens += self.graph.get_token_estimate(atom_id)?;
84 }
85
86 while let Some((atom_id, depth)) = frontier.pop() {
88 if depth >= policy.max_depth {
89 depth_reached = depth_reached.max(depth);
90 continue;
91 }
92
93 if included.len() >= policy.max_atoms || total_tokens >= policy.max_tokens {
94 break;
95 }
96
97 let deps = self.graph.get_dependencies(&atom_id)?;
98 for (edge_type, dep_id) in deps {
99 if policy.follow_edges.contains(&edge_type) && !included.contains(&dep_id) {
100 let dep_tokens = self.graph.get_token_estimate(&dep_id)?;
101 if total_tokens + dep_tokens <= policy.max_tokens {
102 included.insert(dep_id.clone());
103 total_tokens += dep_tokens;
104 frontier.push((dep_id, depth + 1));
105 }
106 }
107 }
108 }
109
110 let truncated = included.len() >= policy.max_atoms || total_tokens >= policy.max_tokens;
112
113 Ok(ExpansionSimulation {
114 included_atoms: included.into_iter().collect(),
115 total_tokens,
116 max_depth_reached: depth_reached,
117 truncated,
118 })
119 }
120
121 fn calculate_priority(&self, edge_type: &EdgeType) -> DependencyPriority {
122 match edge_type {
123 EdgeType::Imports => DependencyPriority::Critical,
124 EdgeType::TypeRef => DependencyPriority::High,
125 EdgeType::Calls => DependencyPriority::Medium,
126 EdgeType::Implements => DependencyPriority::High,
127 EdgeType::Extends => DependencyPriority::High,
128 EdgeType::GenericRef => DependencyPriority::High,
129 EdgeType::ComposedOf => DependencyPriority::Low,
130 EdgeType::Exports => DependencyPriority::Low,
131 EdgeType::MacroUse => DependencyPriority::Medium,
132 EdgeType::Tests => DependencyPriority::Low,
133 EdgeType::DocRef => DependencyPriority::Low,
134 }
135 }
136}
137
138#[derive(Debug)]
139pub struct ExpansionSimulation {
140 pub included_atoms: Vec<String>,
141 pub total_tokens: usize,
142 pub max_depth_reached: usize,
143 pub truncated: bool,
144}