1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
//! `_index.json`: the merge map for orchestrating agents.
use std::collections::BTreeMap;
use sealmap_model::{Codebase, CodebaseStats, Confidence, ContentHash, Fingerprint, SourcePath, Span, SymbolId};
use serde::{Deserialize, Serialize};
/// Kind of diagram fragment.
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum FragmentKind {
/// Per-file `classDiagram`.
Structure,
/// Per-callable `sequenceDiagram`.
Sequence,
}
/// One call made by a sequence fragment.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct CallRef {
/// Callee id.
pub target: SymbolId,
/// Resolution confidence.
pub confidence: Confidence,
/// 1-based line of the call site.
pub line: u32,
/// Id of the fragment that renders the callee's own sequence, if any.
/// Following these links is how an orchestrator inlines or stitches
/// sequences across files.
#[serde(skip_serializing_if = "Option::is_none")]
pub expands: Option<String>,
}
/// One diagram inside a document.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct FragmentEntry {
/// Fragment id: the symbol id for sequences, `structure:<file>` for
/// structure diagrams. Unique across the corpus.
pub id: String,
/// Diagram kind.
pub kind: FragmentKind,
/// Document holding it.
pub document: SourcePath,
/// Symbol the fragment describes (callable or module).
pub symbol: SymbolId,
/// Source span of that symbol.
pub span: Span,
/// The symbol's contract fingerprint (see `sealmap_model::Symbol`).
pub sig_hash: Fingerprint,
/// The symbol's implementation fingerprint.
pub body_hash: Fingerprint,
/// Mermaid ids of participants / classes, in diagram order.
pub participants: Vec<SymbolId>,
/// Calls, in source order (sequence fragments only).
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub calls: Vec<CallRef>,
/// Messages elided by `max_messages` (0 when complete).
#[serde(default, skip_serializing_if = "is_zero")]
pub truncated: usize,
/// Hash of the fragment's Mermaid text, for caching merged results.
pub hash: ContentHash,
}
fn is_zero(n: &usize) -> bool {
*n == 0
}
/// One document (one per source file).
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct DocumentEntry {
/// Source file.
pub source: SourcePath,
/// Document path.
pub document: SourcePath,
/// Module the source defines.
pub module: SymbolId,
/// Hash of the source the document was generated from.
pub source_hash: ContentHash,
/// Hash of the document text.
pub document_hash: ContentHash,
/// Fragments in document order.
pub fragments: Vec<FragmentEntry>,
}
/// The corpus index.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct Index {
/// [`crate::CORPUS_SCHEMA_VERSION`].
pub schema_version: u32,
/// Generator name and version, e.g. `sealmap-corpus 0.1.0`.
pub generator: String,
/// Codebase name.
pub codebase: String,
/// Model statistics.
pub stats: CodebaseStats,
/// Documents in source-path order.
pub documents: Vec<DocumentEntry>,
}
impl Index {
pub(crate) fn new(cb: &Codebase) -> Self {
Self {
schema_version: crate::CORPUS_SCHEMA_VERSION,
generator: format!("sealmap-corpus {}", env!("CARGO_PKG_VERSION")),
codebase: cb.name.clone(),
stats: cb.stats(),
documents: Vec::new(),
}
}
/// Fill in [`CallRef::expands`] once all fragments are known.
pub(crate) fn link_expansions(&mut self) {
let seqs: BTreeMap<String, ()> =
self.fragments().filter(|f| f.kind == FragmentKind::Sequence).map(|f| (f.id.clone(), ())).collect();
for doc in &mut self.documents {
for frag in &mut doc.fragments {
for call in &mut frag.calls {
let target = call.target.to_string();
if seqs.contains_key(&target) {
call.expands = Some(target);
}
}
}
}
}
/// All fragments in document order.
pub fn fragments(&self) -> impl Iterator<Item = &FragmentEntry> {
self.documents.iter().flat_map(|d| d.fragments.iter())
}
/// Look up a fragment by id.
pub fn fragment(&self, id: &str) -> Option<&FragmentEntry> {
self.fragments().find(|f| f.id == id)
}
}