Skip to main content

brainwires_rag/code_analysis/
mod.rs

1//! Code relationships module for definition/reference tracking and call graphs.
2//!
3//! This module provides capabilities for understanding code relationships:
4//! - Find where symbols are defined
5//! - Find all references to a symbol
6//! - Build call graphs for functions/methods
7//!
8//! Uses RepoMap (tree-sitter AST-based) for all languages.
9//!
10//! ## Usage
11//!
12//! ```ignore
13//! use brainwires_rag::code_analysis::{HybridRelationsProvider, RelationsProvider};
14//!
15//! let provider = HybridRelationsProvider::new()?;
16//! let definitions = provider.extract_definitions(&file_info)?;
17//! let references = provider.extract_references(&file_info, &symbol_index)?;
18//! ```
19
20pub mod repomap;
21pub mod storage;
22pub mod types;
23
24use anyhow::Result;
25
26pub use types::{
27    CallEdge, CallGraphNode, Definition, DefinitionResult, PrecisionLevel, Reference,
28    ReferenceKind, ReferenceResult, SymbolId, SymbolInfo, SymbolKind, Visibility,
29};
30
31use crate::rag::indexer::FileInfo;
32use std::collections::HashMap;
33
34/// Trait for extracting code relationships from source files.
35///
36/// Implementors of this trait can extract symbol definitions and references
37/// from source code files.
38pub trait RelationsProvider: Send + Sync {
39    /// Extract definitions from a file.
40    ///
41    /// Returns a list of all symbol definitions (functions, classes, etc.)
42    /// found in the given file.
43    fn extract_definitions(&self, file_info: &FileInfo) -> Result<Vec<Definition>>;
44
45    /// Extract references from a file.
46    ///
47    /// `symbol_index` maps symbol names to their definitions, used for
48    /// resolving which symbol a reference points to.
49    fn extract_references(
50        &self,
51        file_info: &FileInfo,
52        symbol_index: &HashMap<String, Vec<Definition>>,
53    ) -> Result<Vec<Reference>>;
54
55    /// Check if this provider supports the given language.
56    fn supports_language(&self, language: &str) -> bool;
57
58    /// Get the precision level of this provider for the given language.
59    fn precision_level(&self, language: &str) -> PrecisionLevel;
60}
61
62/// Hybrid provider that selects the best available provider per language.
63///
64/// Currently delegates to RepoMap (tree-sitter AST-based) for all languages.
65pub struct HybridRelationsProvider {
66    repomap: repomap::RepoMapProvider,
67}
68
69impl HybridRelationsProvider {
70    /// Create a new hybrid relations provider.
71    pub fn new() -> Result<Self> {
72        Ok(Self {
73            repomap: repomap::RepoMapProvider::new(),
74        })
75    }
76}
77
78impl RelationsProvider for HybridRelationsProvider {
79    fn extract_definitions(&self, file_info: &FileInfo) -> Result<Vec<Definition>> {
80        self.repomap.extract_definitions(file_info)
81    }
82
83    fn extract_references(
84        &self,
85        file_info: &FileInfo,
86        symbol_index: &HashMap<String, Vec<Definition>>,
87    ) -> Result<Vec<Reference>> {
88        self.repomap.extract_references(file_info, symbol_index)
89    }
90
91    fn supports_language(&self, language: &str) -> bool {
92        self.repomap.supports_language(language)
93    }
94
95    fn precision_level(&self, language: &str) -> PrecisionLevel {
96        self.repomap.precision_level(language)
97    }
98}
99
100/// Configuration for relations extraction
101#[derive(Debug, Clone)]
102pub struct RelationsConfig {
103    /// Whether relations extraction is enabled
104    pub enabled: bool,
105    /// Maximum call graph traversal depth
106    pub max_call_depth: usize,
107}
108
109impl Default for RelationsConfig {
110    fn default() -> Self {
111        Self {
112            enabled: true,
113            max_call_depth: 3,
114        }
115    }
116}
117
118#[cfg(test)]
119mod tests {
120    use super::*;
121
122    #[test]
123    fn test_hybrid_provider_creation() {
124        let provider = HybridRelationsProvider::new().unwrap();
125        assert!(provider.supports_language("Rust"));
126        assert!(provider.supports_language("Python"));
127        assert!(provider.supports_language("Unknown"));
128    }
129
130    #[test]
131    fn test_precision_level() {
132        let provider = HybridRelationsProvider::new().unwrap();
133        assert_eq!(provider.precision_level("Rust"), PrecisionLevel::Medium);
134        assert_eq!(provider.precision_level("Python"), PrecisionLevel::Medium);
135    }
136
137    #[test]
138    fn test_relations_config_default() {
139        let config = RelationsConfig::default();
140        assert!(config.enabled);
141        assert_eq!(config.max_call_depth, 3);
142    }
143}