Skip to main content

code_kb_core/
models.rs

1use serde::{Deserialize, Serialize};
2
3#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
4pub struct Symbol {
5    pub symbol_id: String,
6    pub file_id: String,
7    pub path: String,
8    pub language: String,
9    pub name: String,
10    pub kind: String,
11    pub signature: Option<String>,
12    pub doc_comment: Option<String>,
13    pub visibility: Option<String>,
14    pub parent_symbol_id: Option<String>,
15    pub start_line: usize,
16    pub start_column: usize,
17    pub end_line: usize,
18    pub end_column: usize,
19    pub start_byte: usize,
20    pub end_byte: usize,
21    pub body_start_line: Option<usize>,
22    pub body_start_column: Option<usize>,
23    pub body_end_line: Option<usize>,
24    pub body_end_column: Option<usize>,
25    pub body_start_byte: Option<usize>,
26    pub body_end_byte: Option<usize>,
27    pub body_hash: Option<String>,
28    pub semantic_group: Option<String>,
29    pub is_test: bool,
30    pub test_container: bool,
31}
32
33impl Symbol {
34    /// Helper to compute hidden line count in implementation body if present.
35    pub fn hidden_body_line_count(&self) -> Option<usize> {
36        match (self.body_start_line, self.body_end_line) {
37            (Some(start), Some(end)) if end >= start => Some(end - start + 1),
38            _ => None,
39        }
40    }
41}
42
43#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
44pub struct FileFact {
45    pub file_id: String,
46    pub path: String,
47    pub language: String,
48    pub content_hash: String,
49    pub content_bytes: i64,
50    pub line_count: Option<i64>,
51    pub indexed_at: String,
52}
53
54#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
55pub struct ReferenceSite {
56    pub from_symbol_name: String,
57    pub from_symbol_id: String,
58    pub to_symbol_name: String,
59    pub kind: String,
60    pub path: String,
61    pub start_line: Option<usize>,
62    pub start_column: Option<usize>,
63    #[serde(default, skip_serializing_if = "Option::is_none")]
64    pub occurrences: Option<usize>,
65    /// For a callee: the definitions the call can reach, as `Owner.name` (path:line).
66    #[serde(default, skip_serializing_if = "Option::is_none")]
67    pub target: Option<String>,
68    /// Text-only owner context for generated lambda names; omitted from JSON.
69    #[serde(skip)]
70    pub enclosing_symbol_name: Option<String>,
71}
72
73/// How well the index evidence ties a name site to the selected definition.
74#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, Hash)]
75#[serde(rename_all = "snake_case")]
76pub enum SiteLabel {
77    Proven,
78    Likely,
79    Unresolved,
80    Excluded,
81}
82
83impl SiteLabel {
84    pub const ALL: [SiteLabel; 4] = [
85        SiteLabel::Proven,
86        SiteLabel::Likely,
87        SiteLabel::Unresolved,
88        SiteLabel::Excluded,
89    ];
90
91    pub fn as_str(self) -> &'static str {
92        match self {
93            SiteLabel::Proven => "proven",
94            SiteLabel::Likely => "likely",
95            SiteLabel::Unresolved => "unresolved",
96            SiteLabel::Excluded => "excluded",
97        }
98    }
99
100    /// Position in the report order: proven, likely, unresolved, excluded.
101    pub fn rank(self) -> usize {
102        match self {
103            SiteLabel::Proven => 0,
104            SiteLabel::Likely => 1,
105            SiteLabel::Unresolved => 2,
106            SiteLabel::Excluded => 3,
107        }
108    }
109}
110
111/// One line of the project that holds the target's name, with the evidence that ties it to the
112/// target. `end_line` is set only when the site spans more than its first line.
113#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
114pub struct LabeledSite {
115    #[serde(flatten)]
116    pub site: ReferenceSite,
117    pub label: SiteLabel,
118    pub basis: String,
119    #[serde(default, skip_serializing_if = "Option::is_none")]
120    pub end_line: Option<usize>,
121    /// The number of arguments the call passes, when julie recorded it.
122    #[serde(default, skip_serializing_if = "Option::is_none")]
123    pub arg_count: Option<u32>,
124}
125
126/// Lines that hold the target's name as a whole word but have no site, definition, or import.
127#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
128pub struct TextOnlyLines {
129    pub path: String,
130    pub lines: Vec<usize>,
131}
132
133/// What the whole-word text scan covered.
134#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
135pub struct TextScanSummary {
136    pub files_scanned: usize,
137    pub files_total: usize,
138    pub complete: bool,
139    pub elapsed_ms: u64,
140    /// Set to the class name when the scan did not run because the target is a constructor,
141    /// whose call sites spell the class name.
142    #[serde(default, skip_serializing_if = "Option::is_none")]
143    pub skipped: Option<String>,
144}
145
146#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
147pub struct ReferenceCounts {
148    pub sites: usize,
149    pub proven: usize,
150    pub likely: usize,
151    pub unresolved: usize,
152    pub excluded: usize,
153    pub text_lines: usize,
154    pub site_lines: usize,
155    pub definition_lines: usize,
156    pub import_lines: usize,
157    pub text_only_lines: usize,
158}
159
160#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
161pub struct ReferenceTarget {
162    pub symbol_id: String,
163    pub name: String,
164    /// `Owner.name` for a member of a class or another code type, else `name`.
165    pub display_name: String,
166    pub kind: String,
167    pub path: String,
168    pub line: usize,
169    pub definitions_with_name: usize,
170}
171
172/// A method of the same name in another type of the target owner's inheritance chain: an
173/// override of the target, or a method that the target overrides.
174#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
175pub struct OverrideRow {
176    pub symbol_id: String,
177    pub owner: String,
178    pub name: String,
179    pub path: String,
180    pub line: usize,
181}
182
183/// A place that builds a value of the target type: a call or `new`, or a Rust or Go struct literal.
184#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
185pub struct ConstructionSite {
186    pub path: String,
187    pub line: usize,
188}
189
190/// A function that names some variants of the enum target, where a `match` may need a new arm.
191/// `caller` and `caller_id` are empty for code outside any symbol; `line` is the first use.
192#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
193pub struct VariantUse {
194    pub caller: String,
195    pub caller_id: String,
196    pub path: String,
197    pub line: usize,
198    pub variants_used: usize,
199    pub variants_total: usize,
200}
201
202/// A route or other framework fact that registers the target function as a handler. `template`
203/// is the route path (with any prefix joined) when the fact has one; `by_name` is true when only
204/// the name ties the fact to the target.
205#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
206pub struct Registration {
207    pub pattern_id: String,
208    pub verb: Option<String>,
209    pub template: Option<String>,
210    pub path: String,
211    pub line: usize,
212    pub by_name: bool,
213}
214
215/// A test that reaches the target through a proven or likely site: a test function or method, or
216/// the test that holds a helper which has the site. `command` runs that test when the markers of
217/// the project name the runner.
218#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
219pub struct CallingTest {
220    pub symbol_id: String,
221    pub name: String,
222    pub path: String,
223    pub line: usize,
224    #[serde(default, skip_serializing_if = "Option::is_none")]
225    pub command: Option<String>,
226    /// Only `likely` sites with the basis `name only` tie the test to the target.
227    #[serde(default, skip_serializing_if = "std::ops::Not::not")]
228    pub name_only: bool,
229}
230
231/// Every indexed site of one definition's name, labeled by evidence, with a whole-word text count
232/// that accounts for each line of the name. `sites` is complete and sorted by label (proven,
233/// likely, unresolved, excluded), then path, line, and column. The section arrays come after
234/// labeling, never change a label, and are left out of JSON when empty.
235#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
236pub struct ReferenceReport {
237    pub target: ReferenceTarget,
238    pub counts: ReferenceCounts,
239    pub sites: Vec<LabeledSite>,
240    pub text_only: Vec<TextOnlyLines>,
241    pub text_scan: TextScanSummary,
242    pub cannot_see: Vec<String>,
243    pub imports: Vec<(String, usize)>,
244    /// Methods of subtypes that calls through the target can reach.
245    #[serde(default, skip_serializing_if = "Vec::is_empty")]
246    pub overrides: Vec<OverrideRow>,
247    /// Methods of ancestor types that the target overrides, nearest first.
248    #[serde(default, skip_serializing_if = "Vec::is_empty")]
249    pub overridden: Vec<OverrideRow>,
250    /// Places that build a value of a class, struct, or record target, one row per line.
251    #[serde(default, skip_serializing_if = "Vec::is_empty")]
252    pub constructions: Vec<ConstructionSite>,
253    /// Functions that name variants of an enum target, most variants first.
254    #[serde(default, skip_serializing_if = "Vec::is_empty")]
255    pub variant_uses: Vec<VariantUse>,
256    /// Route facts and other framework facts that register the target function as a handler.
257    #[serde(default, skip_serializing_if = "Vec::is_empty")]
258    pub registrations: Vec<Registration>,
259    /// Tests with a proven or likely site of the target, each with the command that runs it.
260    #[serde(default, skip_serializing_if = "Vec::is_empty")]
261    pub tests: Vec<CallingTest>,
262}
263
264pub(crate) fn has_generated_lambda_name(name: &str) -> bool {
265    name.strip_prefix("lambda_")
266        .is_some_and(|line| !line.is_empty() && line.bytes().all(|byte| byte.is_ascii_digit()))
267}
268
269pub(crate) fn is_generated_lambda_name(
270    name: &str,
271    language: &str,
272    kind: &str,
273    signature: Option<&str>,
274) -> bool {
275    has_generated_lambda_name(name)
276        && language == "python"
277        && kind == "function"
278        && signature.is_some_and(|signature| signature.starts_with("lambda"))
279}
280
281#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
282pub struct StructuralFact {
283    pub structural_fact_id: String,
284    pub path: String,
285    pub language: String,
286    pub pattern_id: String,
287    pub capture_name: String,
288    pub node_kind: String,
289    pub key: Option<String>,
290    pub metadata: Option<serde_json::Value>,
291    pub containing_symbol_name: Option<String>,
292    pub start_line: usize,
293    pub end_line: usize,
294    pub confidence: f64,
295}
296
297#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
298pub struct LiteralFact {
299    pub literal_id: String,
300    pub path: String,
301    pub literal_text: String,
302    pub kind: String,
303    pub carrier: Option<String>,
304    pub start_line: usize,
305    pub containing_symbol_name: Option<String>,
306}
307
308#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
309pub struct TypeFact {
310    pub type_fact_id: String,
311    pub symbol_id: String,
312    pub language: String,
313    pub resolved_type: String,
314    pub generic_params: Option<String>,
315}
316
317#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
318pub struct ContextSlice {
319    pub target_symbol: Symbol,
320    pub target_body: String,
321    pub callee_signatures: Vec<String>,
322    pub related_types: Vec<String>,
323    pub related_tests: Vec<Symbol>,
324}
325
326/// One `search` hit. `score` is the rerank score; `explain` is present only when the
327/// caller asked for the breakdown.
328#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
329pub struct SymbolSearchResult {
330    pub symbol: Symbol,
331    pub score: f64,
332    pub snippet: Option<String>,
333    #[serde(default, skip_serializing_if = "Option::is_none")]
334    pub explain: Option<SearchExplain>,
335}
336
337/// Rerank breakdown for one `search` hit: which recall branches admitted the row, the name
338/// tier with its match strength, every score part, the query terms with the field that
339/// credited each and each term's rarity weight, and the timer over the whole candidate set.
340/// `score` is `term_score + field_diversity_bonus + name_bonus + kind_prior + path_role + documentation + test_intent + nested`.
341#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
342pub struct SearchExplain {
343    pub bm25: Option<f64>,
344    pub branches: Vec<String>,
345    pub name_tier: String,
346    pub name_strength: u32,
347    pub terms: Vec<(String, String, f64)>,
348    pub term_score: f64,
349    #[serde(default)]
350    pub field_diversity_bonus: f64,
351    pub name_bonus: f64,
352    pub kind_prior: f64,
353    pub path_role: f64,
354    pub documentation: f64,
355    pub test_intent: f64,
356    #[serde(default)]
357    pub nested: f64,
358    pub word_weights: Vec<(String, f64)>,
359    pub candidates: usize,
360    pub rerank_us: u128,
361}
362
363#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
364pub struct BlastRadiusResult {
365    pub seed_type: String,
366    pub seeds: Vec<String>,
367    pub likely_tests: Vec<TestTarget>,
368    pub impacted_symbols: Vec<ImpactedSymbol>,
369    pub likely_tests_truncated: bool,
370    pub impacted_symbols_truncated: bool,
371    #[serde(default)]
372    pub traversal_ceiling_reached: bool,
373    /// The request already used the largest `limit`, so a larger one cannot show more rows.
374    #[serde(default)]
375    pub limit_at_maximum: bool,
376    /// Likely tests found before `limit` cut the list.
377    #[serde(default)]
378    pub likely_tests_found: usize,
379    /// Impacted symbols found before `limit` cut the list.
380    #[serde(default)]
381    pub impacted_symbols_found: usize,
382    /// The depth limit of a walk that has callers beyond it, so a deeper walk may find more.
383    #[serde(default)]
384    pub stopped_at_depth: Option<usize>,
385}
386
387#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
388pub struct TestTarget {
389    pub name: String,
390    pub path: String,
391    pub line: usize,
392    pub reason: String,
393}
394
395#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
396pub struct ImpactedSymbol {
397    pub name: String,
398    pub kind: String,
399    pub path: String,
400    pub line: usize,
401    pub depth: usize,
402    /// Text-only owner context for generated lambda names; omitted from JSON.
403    #[serde(skip)]
404    pub enclosing_symbol_name: Option<String>,
405}