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
73pub(crate) fn has_generated_lambda_name(name: &str) -> bool {
74    name.strip_prefix("lambda_")
75        .is_some_and(|line| !line.is_empty() && line.bytes().all(|byte| byte.is_ascii_digit()))
76}
77
78pub(crate) fn is_generated_lambda_name(
79    name: &str,
80    language: &str,
81    kind: &str,
82    signature: Option<&str>,
83) -> bool {
84    has_generated_lambda_name(name)
85        && language == "python"
86        && kind == "function"
87        && signature.is_some_and(|signature| signature.starts_with("lambda"))
88}
89
90#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
91pub struct StructuralFact {
92    pub structural_fact_id: String,
93    pub path: String,
94    pub language: String,
95    pub pattern_id: String,
96    pub capture_name: String,
97    pub node_kind: String,
98    pub key: Option<String>,
99    pub metadata: Option<serde_json::Value>,
100    pub containing_symbol_name: Option<String>,
101    pub start_line: usize,
102    pub end_line: usize,
103    pub confidence: f64,
104}
105
106#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
107pub struct LiteralFact {
108    pub literal_id: String,
109    pub path: String,
110    pub literal_text: String,
111    pub kind: String,
112    pub carrier: Option<String>,
113    pub start_line: usize,
114    pub containing_symbol_name: Option<String>,
115}
116
117#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
118pub struct TypeFact {
119    pub type_fact_id: String,
120    pub symbol_id: String,
121    pub language: String,
122    pub resolved_type: String,
123    pub generic_params: Option<String>,
124}
125
126#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
127pub struct ContextSlice {
128    pub target_symbol: Symbol,
129    pub target_body: String,
130    pub callee_signatures: Vec<String>,
131    pub related_types: Vec<String>,
132    pub related_tests: Vec<Symbol>,
133}
134
135/// One `search` hit. `score` is the rerank score; `explain` is present only when the
136/// caller asked for the breakdown.
137#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
138pub struct SymbolSearchResult {
139    pub symbol: Symbol,
140    pub score: f64,
141    pub snippet: Option<String>,
142    #[serde(default, skip_serializing_if = "Option::is_none")]
143    pub explain: Option<SearchExplain>,
144}
145
146/// Rerank breakdown for one `search` hit: which recall branches admitted the row, the name
147/// tier with its match strength, every score part, the query terms with the field that
148/// credited each and each term's rarity weight, and the timer over the whole candidate set.
149/// `score` is `term_score + field_diversity_bonus + name_bonus + kind_prior + path_role + documentation + test_intent + nested`.
150#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
151pub struct SearchExplain {
152    pub bm25: Option<f64>,
153    pub branches: Vec<String>,
154    pub name_tier: String,
155    pub name_strength: u32,
156    pub terms: Vec<(String, String, f64)>,
157    pub term_score: f64,
158    #[serde(default)]
159    pub field_diversity_bonus: f64,
160    pub name_bonus: f64,
161    pub kind_prior: f64,
162    pub path_role: f64,
163    pub documentation: f64,
164    pub test_intent: f64,
165    #[serde(default)]
166    pub nested: f64,
167    pub word_weights: Vec<(String, f64)>,
168    pub candidates: usize,
169    pub rerank_us: u128,
170}
171
172#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
173pub struct BlastRadiusResult {
174    pub seed_type: String,
175    pub seeds: Vec<String>,
176    pub likely_tests: Vec<TestTarget>,
177    pub impacted_symbols: Vec<ImpactedSymbol>,
178    pub likely_tests_truncated: bool,
179    pub impacted_symbols_truncated: bool,
180    #[serde(default)]
181    pub traversal_ceiling_reached: bool,
182    pub test_file_ceiling_reached: bool,
183    /// The request already used the largest `limit`, so a larger one cannot show more rows.
184    #[serde(default)]
185    pub limit_at_maximum: bool,
186    /// Likely tests found before `limit` cut the list.
187    #[serde(default)]
188    pub likely_tests_found: usize,
189    /// Impacted symbols found before `limit` cut the list.
190    #[serde(default)]
191    pub impacted_symbols_found: usize,
192}
193
194#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
195pub struct TestTarget {
196    pub name: String,
197    pub path: String,
198    pub line: usize,
199    pub reason: String,
200}
201
202#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
203pub struct ImpactedSymbol {
204    pub name: String,
205    pub kind: String,
206    pub path: String,
207    pub line: usize,
208    pub depth: usize,
209    /// Text-only owner context for generated lambda names; omitted from JSON.
210    #[serde(skip)]
211    pub enclosing_symbol_name: Option<String>,
212}