use super::graph_v2::{CodeEdgeV2, CodeGraphV2, MatchExprDataV2};
use crate::symbol::{FileId, SymbolId, SymbolPath, SymbolRegistry};
use crate::SymbolKind;
pub struct GraphBuilderV2<'a> {
graph: CodeGraphV2,
registry: &'a mut SymbolRegistry,
}
impl<'a> GraphBuilderV2<'a> {
pub fn new(registry: &'a mut SymbolRegistry) -> Self {
Self {
graph: CodeGraphV2::new(),
registry,
}
}
pub fn with_capacity(registry: &'a mut SymbolRegistry, nodes: usize, edges: usize) -> Self {
Self {
graph: CodeGraphV2::with_capacity(nodes, edges),
registry,
}
}
pub fn add_symbol(
&mut self,
path: SymbolPath,
kind: SymbolKind,
) -> Result<SymbolId, crate::symbol::RegistrationError> {
let id = self.registry.register(path, kind)?;
self.graph.add_node(id);
self.graph.add_to_kind_index(id, kind);
Ok(id)
}
pub fn add_existing_symbol(&mut self, id: SymbolId, kind: SymbolKind) {
self.graph.add_node(id);
self.graph.add_to_kind_index(id, kind);
}
pub fn add_crate_root(
&mut self,
path: SymbolPath,
) -> Result<SymbolId, crate::symbol::RegistrationError> {
let id = self.registry.register(path, SymbolKind::Mod)?;
self.graph.add_crate_root(id);
self.graph.add_to_kind_index(id, SymbolKind::Mod);
Ok(id)
}
pub fn add_existing_crate_root(&mut self, id: SymbolId) {
self.graph.add_crate_root(id);
self.graph.add_to_kind_index(id, SymbolKind::Mod);
}
pub fn add_contains(&mut self, parent: SymbolId, child: SymbolId) {
self.graph.add_edge(parent, child, CodeEdgeV2::Contains);
}
pub fn add_call(&mut self, caller: SymbolId, callee: SymbolId) {
self.graph.add_edge(caller, callee, CodeEdgeV2::Calls);
}
pub fn add_implements(&mut self, implementor: SymbolId, trait_or_type: SymbolId) {
self.graph
.add_edge(implementor, trait_or_type, CodeEdgeV2::Implements);
}
pub fn add_match_expr(
&mut self,
function_id: SymbolId,
file_id: FileId,
enum_id: SymbolId,
offset: u32,
line: u32,
) {
self.graph.add_match_expr(
function_id,
MatchExprDataV2 {
file_id,
enum_id,
offset,
line,
},
);
}
pub fn build(self) -> CodeGraphV2 {
self.graph
}
pub fn registry(&self) -> &SymbolRegistry {
self.registry
}
pub fn registry_mut(&mut self) -> &mut SymbolRegistry {
self.registry
}
pub fn graph(&self) -> &CodeGraphV2 {
&self.graph
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_builder_basic() {
let mut registry = SymbolRegistry::new();
let mut builder = GraphBuilderV2::new(&mut registry);
let foo = builder
.add_symbol(
SymbolPath::parse("mylib::foo").unwrap(),
SymbolKind::Function,
)
.unwrap();
let bar = builder
.add_symbol(
SymbolPath::parse("mylib::bar").unwrap(),
SymbolKind::Function,
)
.unwrap();
builder.add_call(foo, bar);
let graph = builder.build();
assert_eq!(graph.node_count(), 2);
assert_eq!(graph.edge_count(), 1);
}
#[test]
fn test_builder_module_structure() {
let mut registry = SymbolRegistry::new();
let mut builder = GraphBuilderV2::new(&mut registry);
let crate_root = builder
.add_crate_root(SymbolPath::parse("mylib").unwrap())
.unwrap();
let module = builder
.add_symbol(
SymbolPath::parse("mylib::handlers").unwrap(),
SymbolKind::Mod,
)
.unwrap();
let func = builder
.add_symbol(
SymbolPath::parse("mylib::handlers::handle").unwrap(),
SymbolKind::Function,
)
.unwrap();
builder.add_contains(crate_root, module);
builder.add_contains(module, func);
let graph = builder.build();
let children: Vec<_> = graph.children_of(crate_root).collect();
assert_eq!(children.len(), 1);
let module_children: Vec<_> = graph.children_of(module).collect();
assert_eq!(module_children.len(), 1);
assert_eq!(graph.parent_of(func), Some(module));
}
#[test]
fn test_builder_kind_index() {
let mut registry = SymbolRegistry::new();
let mut builder = GraphBuilderV2::new(&mut registry);
builder
.add_symbol(SymbolPath::parse("mylib::Foo").unwrap(), SymbolKind::Struct)
.unwrap();
builder
.add_symbol(
SymbolPath::parse("mylib::bar").unwrap(),
SymbolKind::Function,
)
.unwrap();
builder
.add_symbol(
SymbolPath::parse("mylib::baz").unwrap(),
SymbolKind::Function,
)
.unwrap();
let graph = builder.build();
let structs: Vec<_> = graph.iter_by_kind(SymbolKind::Struct).collect();
assert_eq!(structs.len(), 1);
let functions: Vec<_> = graph.iter_by_kind(SymbolKind::Function).collect();
assert_eq!(functions.len(), 2);
}
#[test]
fn test_builder_with_capacity() {
let mut registry = SymbolRegistry::new();
let builder = GraphBuilderV2::with_capacity(&mut registry, 100, 200);
let graph = builder.build();
assert!(graph.is_empty());
}
#[test]
fn test_builder_callers_callees() {
let mut registry = SymbolRegistry::new();
let mut builder = GraphBuilderV2::new(&mut registry);
let main = builder
.add_symbol(
SymbolPath::parse("mylib::main").unwrap(),
SymbolKind::Function,
)
.unwrap();
let helper1 = builder
.add_symbol(
SymbolPath::parse("mylib::helper1").unwrap(),
SymbolKind::Function,
)
.unwrap();
let helper2 = builder
.add_symbol(
SymbolPath::parse("mylib::helper2").unwrap(),
SymbolKind::Function,
)
.unwrap();
let util = builder
.add_symbol(
SymbolPath::parse("mylib::util").unwrap(),
SymbolKind::Function,
)
.unwrap();
builder.add_call(main, helper1);
builder.add_call(main, helper2);
builder.add_call(helper1, util);
builder.add_call(helper2, util);
let graph = builder.build();
let main_callees: Vec<_> = graph.callees_of(main).collect();
assert_eq!(main_callees.len(), 2);
let util_callers: Vec<_> = graph.callers_of(util).collect();
assert_eq!(util_callers.len(), 2);
}
#[test]
fn test_builder_implements() {
let mut registry = SymbolRegistry::new();
let mut builder = GraphBuilderV2::new(&mut registry);
let trait_id = builder
.add_symbol(
SymbolPath::parse("mylib::MyTrait").unwrap(),
SymbolKind::Trait,
)
.unwrap();
let impl1 = builder
.add_symbol(SymbolPath::parse("mylib::Foo").unwrap(), SymbolKind::Struct)
.unwrap();
let impl2 = builder
.add_symbol(SymbolPath::parse("mylib::Bar").unwrap(), SymbolKind::Struct)
.unwrap();
builder.add_implements(impl1, trait_id);
builder.add_implements(impl2, trait_id);
let graph = builder.build();
let implementors: Vec<_> = graph.implementors_of(trait_id).collect();
assert_eq!(implementors.len(), 2);
}
}