use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::path::Path;
#[cfg(feature = "mcp")]
use schemars::JsonSchema;
pub mod extractor;
pub mod indexer;
pub mod relationships;
pub mod context;
pub use extractor::{SymbolExtractor, extract_symbols_from_file};
pub use indexer::{SymbolIndex, SymbolIndexStore};
pub use relationships::RelationshipGraph;
pub use context::SymbolContext;
#[derive(Debug, Clone, Serialize, Deserialize)]
#[cfg_attr(feature = "mcp", derive(schemars::JsonSchema))]
pub struct Symbol {
pub id: String,
pub name: String,
pub kind: SymbolKind,
pub file_path: String,
pub line: usize,
pub column: usize,
pub end_line: usize,
pub signature: String,
pub documentation: Option<String>,
pub visibility: SymbolVisibility,
pub parent: Option<String>,
pub type_info: Option<TypeInfo>,
pub generics: Vec<String>,
pub annotations: Vec<String>,
pub attributes: Vec<String>,
pub metadata: HashMap<String, String>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "mcp", derive(schemars::JsonSchema))]
pub enum SymbolKind {
Module,
Package,
Namespace,
Class,
Interface,
Trait,
Struct,
Enum,
Union,
TypeAlias,
Function,
Method,
Constructor,
Destructor,
Variable,
Field,
Property,
Parameter,
Constant,
Macro,
Import,
Export,
Unknown,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[cfg_attr(feature = "mcp", derive(schemars::JsonSchema))]
pub enum SymbolVisibility {
Public,
Private,
Protected,
Internal,
PackagePrivate,
FilePrivate,
None,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[cfg_attr(feature = "mcp", derive(schemars::JsonSchema))]
pub struct TypeInfo {
pub name: String,
pub parameters: Vec<String>,
pub return_type: Option<Box<TypeInfo>>,
pub nullable: bool,
pub kind: TypeKind,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[cfg_attr(feature = "mcp", derive(schemars::JsonSchema))]
pub enum TypeKind {
Primitive,
Object,
Array,
Map,
Tuple,
Function,
Union,
Intersection,
Unknown,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FileSymbols {
pub path: String,
pub language: String,
pub symbols: Vec<Symbol>,
pub imports: Vec<ImportInfo>,
pub exports: Vec<ExportInfo>,
pub file_doc: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ImportInfo {
pub path: String,
pub symbols: Vec<String>,
pub alias: Option<String>,
pub is_wildcard: bool,
pub line: usize,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ExportInfo {
pub name: String,
pub alias: Option<String>,
pub line: usize,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[cfg_attr(feature = "mcp", derive(schemars::JsonSchema))]
pub struct SymbolSearchResult {
pub symbol: Symbol,
pub score: f64,
pub context_before: Vec<String>,
pub context_after: Vec<String>,
pub related_symbols: Vec<SymbolRelation>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[cfg_attr(feature = "mcp", derive(schemars::JsonSchema))]
pub struct SymbolRelation {
pub symbol_id: String,
pub relation_type: SymbolRelationType,
pub confidence: f64,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[cfg_attr(feature = "mcp", derive(schemars::JsonSchema))]
pub enum SymbolRelationType {
Inherits,
Implements,
Calls,
CalledBy,
Contains,
ContainedIn,
References,
ReferencedBy,
Overrides,
OverriddenBy,
Instantiates,
InstantiatedBy,
ParameterOf,
ReturnTypeOf,
FieldOf,
MethodOf,
Imports,
ImportedBy,
Exports,
ExportedBy,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct IndexStats {
pub total_symbols: usize,
pub total_files: usize,
pub symbols_by_kind: HashMap<String, usize>,
pub symbols_by_language: HashMap<String, usize>,
pub index_size_bytes: u64,
pub last_updated: chrono::DateTime<chrono::Utc>,
}
impl Symbol {
pub fn create_id(file_path: &str, name: &str, kind: &SymbolKind, line: usize) -> String {
format!("{}:{}:{}:{}",
file_path.replace("/", "_").replace(".", "_"),
name,
format!("{:?}", kind),
line
)
}
pub fn qualified_name(&self) -> String {
if let Some(ref parent) = self.parent {
format!("{}.{}", parent, self.name)
} else {
self.name.clone()
}
}
pub fn is_public(&self) -> bool {
matches!(self.visibility, SymbolVisibility::Public)
}
pub fn is_type(&self) -> bool {
matches!(
self.kind,
SymbolKind::Class |
SymbolKind::Interface |
SymbolKind::Trait |
SymbolKind::Struct |
SymbolKind::Enum |
SymbolKind::Union
)
}
pub fn is_function(&self) -> bool {
matches!(
self.kind,
SymbolKind::Function |
SymbolKind::Method |
SymbolKind::Constructor
)
}
pub fn display_name(&self) -> String {
if self.signature.is_empty() {
self.name.clone()
} else {
format!("{}{}", self.name, self.signature)
}
}
}
impl std::fmt::Display for SymbolKind {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
SymbolKind::Module => write!(f, "module"),
SymbolKind::Package => write!(f, "package"),
SymbolKind::Namespace => write!(f, "namespace"),
SymbolKind::Class => write!(f, "class"),
SymbolKind::Interface => write!(f, "interface"),
SymbolKind::Trait => write!(f, "trait"),
SymbolKind::Struct => write!(f, "struct"),
SymbolKind::Enum => write!(f, "enum"),
SymbolKind::Union => write!(f, "union"),
SymbolKind::TypeAlias => write!(f, "type"),
SymbolKind::Function => write!(f, "function"),
SymbolKind::Method => write!(f, "method"),
SymbolKind::Constructor => write!(f, "constructor"),
SymbolKind::Destructor => write!(f, "destructor"),
SymbolKind::Variable => write!(f, "variable"),
SymbolKind::Field => write!(f, "field"),
SymbolKind::Property => write!(f, "property"),
SymbolKind::Parameter => write!(f, "parameter"),
SymbolKind::Constant => write!(f, "constant"),
SymbolKind::Macro => write!(f, "macro"),
SymbolKind::Import => write!(f, "import"),
SymbolKind::Export => write!(f, "export"),
SymbolKind::Unknown => write!(f, "unknown"),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_symbol_id_creation() {
let id = Symbol::create_id("src/main.rs", "test_func", &SymbolKind::Function, 42);
assert!(id.contains("src_main_rs"));
assert!(id.contains("test_func"));
assert!(id.contains("42"));
}
#[test]
fn test_qualified_name() {
let symbol = Symbol {
id: "test".to_string(),
name: "method".to_string(),
kind: SymbolKind::Method,
file_path: "test.rs".to_string(),
line: 10,
column: 0,
end_line: 15,
signature: "()".to_string(),
documentation: None,
visibility: SymbolVisibility::Public,
parent: Some("MyClass".to_string()),
type_info: None,
generics: vec![],
annotations: vec![],
attributes: vec![],
metadata: HashMap::new(),
};
assert_eq!(symbol.qualified_name(), "MyClass.method");
}
#[test]
fn test_symbol_type_checks() {
let class_symbol = Symbol {
id: "test".to_string(),
name: "MyClass".to_string(),
kind: SymbolKind::Class,
file_path: "test.rs".to_string(),
line: 10,
column: 0,
end_line: 15,
signature: "".to_string(),
documentation: None,
visibility: SymbolVisibility::Public,
parent: None,
type_info: None,
generics: vec![],
annotations: vec![],
attributes: vec![],
metadata: HashMap::new(),
};
assert!(class_symbol.is_type());
assert!(!class_symbol.is_function());
let func_symbol = Symbol {
id: "test2".to_string(),
name: "my_func".to_string(),
kind: SymbolKind::Function,
file_path: "test.rs".to_string(),
line: 20,
column: 0,
end_line: 25,
signature: "()".to_string(),
documentation: None,
visibility: SymbolVisibility::Public,
parent: None,
type_info: None,
generics: vec![],
annotations: vec![],
attributes: vec![],
metadata: HashMap::new(),
};
assert!(!func_symbol.is_type());
assert!(func_symbol.is_function());
}
}