project_rag/relations/
mod.rs1pub mod repomap;
27pub mod storage;
28pub mod types;
29
30#[cfg(feature = "stack-graphs")]
31pub mod stack_graphs;
32
33use anyhow::Result;
34
35pub use types::{
36 CallEdge, CallGraphNode, Definition, DefinitionResult, PrecisionLevel, Reference,
37 ReferenceKind, ReferenceResult, SymbolId, SymbolInfo, SymbolKind, Visibility,
38};
39
40use crate::indexer::FileInfo;
41use std::collections::HashMap;
42
43pub trait RelationsProvider: Send + Sync {
48 fn extract_definitions(&self, file_info: &FileInfo) -> Result<Vec<Definition>>;
53
54 fn extract_references(
59 &self,
60 file_info: &FileInfo,
61 symbol_index: &HashMap<String, Vec<Definition>>,
62 ) -> Result<Vec<Reference>>;
63
64 fn supports_language(&self, language: &str) -> bool;
66
67 fn precision_level(&self, language: &str) -> PrecisionLevel;
69}
70
71pub struct HybridRelationsProvider {
76 #[cfg(feature = "stack-graphs")]
78 stack_graphs: Option<stack_graphs::StackGraphsProvider>,
79
80 repomap: repomap::RepoMapProvider,
82}
83
84impl HybridRelationsProvider {
85 pub fn new(_enable_stack_graphs: bool) -> Result<Self> {
90 #[cfg(feature = "stack-graphs")]
91 let stack_graphs = if _enable_stack_graphs {
92 match stack_graphs::StackGraphsProvider::new() {
93 Ok(sg) => Some(sg),
94 Err(e) => {
95 tracing::warn!("Failed to initialize stack-graphs: {}", e);
96 None
97 }
98 }
99 } else {
100 None
101 };
102
103 Ok(Self {
104 #[cfg(feature = "stack-graphs")]
105 stack_graphs,
106 repomap: repomap::RepoMapProvider::new(),
107 })
108 }
109
110 fn provider_for_language(&self, _language: &str) -> &dyn RelationsProvider {
112 #[cfg(feature = "stack-graphs")]
113 if let Some(ref sg) = self.stack_graphs {
114 if sg.supports_language(_language) {
115 return sg;
116 }
117 }
118
119 &self.repomap
120 }
121
122 #[cfg(feature = "stack-graphs")]
124 pub fn has_stack_graphs_for(&self, language: &str) -> bool {
125 self.stack_graphs
126 .as_ref()
127 .is_some_and(|sg| sg.supports_language(language))
128 }
129
130 #[cfg(not(feature = "stack-graphs"))]
132 pub fn has_stack_graphs_for(&self, _language: &str) -> bool {
133 false
134 }
135}
136
137impl RelationsProvider for HybridRelationsProvider {
138 fn extract_definitions(&self, file_info: &FileInfo) -> Result<Vec<Definition>> {
139 let language = file_info.language.as_deref().unwrap_or("Unknown");
140 self.provider_for_language(language)
141 .extract_definitions(file_info)
142 }
143
144 fn extract_references(
145 &self,
146 file_info: &FileInfo,
147 symbol_index: &HashMap<String, Vec<Definition>>,
148 ) -> Result<Vec<Reference>> {
149 let language = file_info.language.as_deref().unwrap_or("Unknown");
150 self.provider_for_language(language)
151 .extract_references(file_info, symbol_index)
152 }
153
154 fn supports_language(&self, language: &str) -> bool {
155 self.repomap.supports_language(language)
157 }
158
159 fn precision_level(&self, language: &str) -> PrecisionLevel {
160 #[cfg(feature = "stack-graphs")]
161 if self.has_stack_graphs_for(language) {
162 return PrecisionLevel::High;
163 }
164
165 self.repomap.precision_level(language)
166 }
167}
168
169#[derive(Debug, Clone)]
171pub struct RelationsConfig {
172 pub enabled: bool,
174 pub use_stack_graphs: bool,
176 pub max_call_depth: usize,
178}
179
180impl Default for RelationsConfig {
181 fn default() -> Self {
182 Self {
183 enabled: true,
184 use_stack_graphs: cfg!(feature = "stack-graphs"),
185 max_call_depth: 3,
186 }
187 }
188}
189
190#[cfg(test)]
191mod tests {
192 use super::*;
193
194 #[test]
195 fn test_hybrid_provider_creation() {
196 let provider = HybridRelationsProvider::new(false).unwrap();
197 assert!(provider.supports_language("Rust"));
198 assert!(provider.supports_language("Python"));
199 assert!(provider.supports_language("Unknown"));
200 }
201
202 #[test]
203 fn test_precision_level_without_stack_graphs() {
204 let provider = HybridRelationsProvider::new(false).unwrap();
205 assert_eq!(provider.precision_level("Rust"), PrecisionLevel::Medium);
207 assert_eq!(provider.precision_level("Python"), PrecisionLevel::Medium);
208 }
209
210 #[test]
211 fn test_relations_config_default() {
212 let config = RelationsConfig::default();
213 assert!(config.enabled);
214 assert_eq!(config.max_call_depth, 3);
215 }
216
217 #[test]
218 fn test_has_stack_graphs() {
219 let provider = HybridRelationsProvider::new(false).unwrap();
220 #[cfg(not(feature = "stack-graphs"))]
222 assert!(!provider.has_stack_graphs_for("Python"));
223 }
224}