sealmap_model/
codebase.rs1use std::collections::{BTreeMap, BTreeSet};
2
3use serde::{Deserialize, Serialize};
4
5use crate::path::SourcePath;
6use crate::source::SourceFile;
7use crate::symbol::{Relation, RelationKind, Symbol, SymbolId, SymbolKind};
8
9#[derive(Debug, Clone, PartialEq, Eq, Default, Serialize, Deserialize)]
15pub struct Codebase {
16 pub schema: u32,
18 pub name: String,
20 pub files: BTreeMap<SourcePath, SourceFile>,
22 pub symbols: BTreeMap<SymbolId, Symbol>,
24 pub relations: BTreeSet<Relation>,
26}
27
28impl Codebase {
29 pub fn new(name: impl Into<String>) -> Self {
31 Self { schema: crate::MODEL_SCHEMA_VERSION, name: name.into(), ..Self::default() }
32 }
33
34 pub fn add_file(&mut self, file: SourceFile) {
36 self.files.insert(file.path.clone(), file);
37 }
38
39 pub fn add_symbol(&mut self, symbol: Symbol) {
43 match self.symbols.get_mut(&symbol.id) {
44 Some(existing) => {
45 existing.members.extend(symbol.members);
46 for tag in symbol.tags {
47 if !existing.tags.contains(&tag) {
48 existing.tags.push(tag);
49 }
50 }
51 }
52 None => {
53 self.symbols.insert(symbol.id.clone(), symbol);
54 }
55 }
56 }
57
58 pub fn add_relation(&mut self, relation: Relation) {
60 if relation.from != relation.to {
61 self.relations.insert(relation);
62 }
63 }
64
65 pub fn file(&self, path: &SourcePath) -> Option<&SourceFile> {
67 self.files.get(path)
68 }
69
70 pub fn symbol(&self, id: &SymbolId) -> Option<&Symbol> {
72 self.symbols.get(id)
73 }
74
75 pub fn is_internal(&self, id: &SymbolId) -> bool {
77 self.symbols.contains_key(id)
78 }
79
80 pub fn symbols_in_file<'a>(&'a self, path: &'a SourcePath) -> impl Iterator<Item = &'a Symbol> {
82 self.symbols.values().filter(move |s| &s.file == path)
83 }
84
85 pub fn symbols_of_kind(&self, kind: SymbolKind) -> impl Iterator<Item = &Symbol> {
87 self.symbols.values().filter(move |s| s.kind == kind)
88 }
89
90 pub fn children<'a>(&'a self, id: &'a SymbolId) -> impl Iterator<Item = &'a Symbol> {
92 self.symbols.values().filter(move |s| s.parent.as_ref() == Some(id))
93 }
94
95 pub fn relations_from<'a>(&'a self, id: &'a SymbolId) -> impl Iterator<Item = &'a Relation> {
100 let lower = Relation::new(id.clone(), SymbolId::new(""), RelationKind::Contains, crate::Confidence::Exact);
101 self.relations.range(lower..).take_while(move |r| &r.from == id)
102 }
103
104 pub fn relations_to<'a>(&'a self, id: &'a SymbolId) -> impl Iterator<Item = &'a Relation> {
106 self.relations.iter().filter(move |r| &r.to == id)
107 }
108
109 pub fn relations_of_kind(&self, kind: RelationKind) -> impl Iterator<Item = &Relation> {
111 self.relations.iter().filter(move |r| r.kind == kind)
112 }
113
114 pub fn owner_of(&self, id: &SymbolId) -> Option<SymbolId> {
120 match self.symbols.get(id) {
121 Some(sym) if sym.kind == SymbolKind::Method || sym.kind == SymbolKind::Function => sym.parent.clone(),
122 Some(sym) => Some(sym.id.clone()),
123 None => id.parent(),
124 }
125 }
126
127 pub fn stats(&self) -> CodebaseStats {
129 let mut stats = CodebaseStats {
130 files: self.files.len(),
131 symbols: self.symbols.len(),
132 relations: self.relations.len(),
133 ..CodebaseStats::default()
134 };
135 for sym in self.symbols.values() {
136 if let Some(flow) = &sym.flow {
137 stats.flows += 1;
138 stats.calls += flow.call_count();
139 }
140 }
141 stats
142 }
143}
144
145#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize)]
147pub struct CodebaseStats {
148 pub files: usize,
150 pub symbols: usize,
152 pub relations: usize,
154 pub flows: usize,
156 pub calls: usize,
158}