Skip to main content

ctx/db/
models.rs

1//! Data models for code intelligence.
2
3use serde::{Deserialize, Serialize};
4use std::str::FromStr;
5
6/// Represents a tracked file in the codebase.
7#[derive(Debug, Clone, Serialize, Deserialize)]
8pub struct FileRecord {
9    pub path: String,
10    pub content_hash: String,
11    pub size_bytes: i64,
12    pub language: Option<String>,
13    pub last_indexed: i64,
14}
15
16/// The kind of symbol (function, struct, etc.).
17#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
18#[serde(rename_all = "snake_case")]
19pub enum SymbolKind {
20    Function,
21    Method,
22    Struct,
23    Enum,
24    Trait,
25    Impl,
26    Const,
27    Static,
28    Type,
29    Macro,
30    Module,
31    Field,
32    Variant,
33    Interface,
34    Class,
35    Variable,
36    Parameter,
37}
38
39impl SymbolKind {
40    pub fn as_str(&self) -> &'static str {
41        match self {
42            SymbolKind::Function => "function",
43            SymbolKind::Method => "method",
44            SymbolKind::Struct => "struct",
45            SymbolKind::Enum => "enum",
46            SymbolKind::Trait => "trait",
47            SymbolKind::Impl => "impl",
48            SymbolKind::Const => "const",
49            SymbolKind::Static => "static",
50            SymbolKind::Type => "type",
51            SymbolKind::Macro => "macro",
52            SymbolKind::Module => "module",
53            SymbolKind::Field => "field",
54            SymbolKind::Variant => "variant",
55            SymbolKind::Interface => "interface",
56            SymbolKind::Class => "class",
57            SymbolKind::Variable => "variable",
58            SymbolKind::Parameter => "parameter",
59        }
60    }
61}
62
63impl FromStr for SymbolKind {
64    type Err = ();
65    fn from_str(s: &str) -> Result<Self, Self::Err> {
66        match s {
67            "function" => Ok(SymbolKind::Function),
68            "method" => Ok(SymbolKind::Method),
69            "struct" => Ok(SymbolKind::Struct),
70            "enum" => Ok(SymbolKind::Enum),
71            "trait" => Ok(SymbolKind::Trait),
72            "impl" => Ok(SymbolKind::Impl),
73            "const" => Ok(SymbolKind::Const),
74            "static" => Ok(SymbolKind::Static),
75            "type" => Ok(SymbolKind::Type),
76            "macro" => Ok(SymbolKind::Macro),
77            "module" => Ok(SymbolKind::Module),
78            "field" => Ok(SymbolKind::Field),
79            "variant" => Ok(SymbolKind::Variant),
80            "interface" => Ok(SymbolKind::Interface),
81            "class" => Ok(SymbolKind::Class),
82            "variable" => Ok(SymbolKind::Variable),
83            "parameter" => Ok(SymbolKind::Parameter),
84            _ => Err(()),
85        }
86    }
87}
88
89/// Visibility of a symbol.
90#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize)]
91#[serde(rename_all = "snake_case")]
92pub enum Visibility {
93    Public,
94    #[default]
95    Private,
96    Crate,
97    Super,
98    InPath,
99}
100
101impl Visibility {
102    pub fn as_str(&self) -> &'static str {
103        match self {
104            Visibility::Public => "public",
105            Visibility::Private => "private",
106            Visibility::Crate => "crate",
107            Visibility::Super => "super",
108            Visibility::InPath => "in_path",
109        }
110    }
111}
112
113impl FromStr for Visibility {
114    type Err = ();
115    fn from_str(s: &str) -> Result<Self, Self::Err> {
116        match s {
117            "public" | "pub" => Ok(Visibility::Public),
118            "crate" | "pub(crate)" => Ok(Visibility::Crate),
119            "super" | "pub(super)" => Ok(Visibility::Super),
120            "in_path" => Ok(Visibility::InPath),
121            _ => Ok(Visibility::Private),
122        }
123    }
124}
125
126/// A code symbol (function, struct, etc.).
127#[derive(Debug, Clone, Serialize, Deserialize)]
128pub struct Symbol {
129    /// Unique identifier: 'file_path::symbol_name' or 'file_path::parent::symbol_name'
130    pub id: String,
131    /// Path to the file containing this symbol
132    pub file_path: String,
133    /// Simple name of the symbol
134    pub name: String,
135    /// Fully qualified name (e.g., module::submodule::name)
136    pub qualified_name: Option<String>,
137    /// Kind of symbol
138    pub kind: SymbolKind,
139    /// Visibility
140    pub visibility: Visibility,
141    /// Function/method signature or type definition
142    pub signature: Option<String>,
143    /// Brief one-line description (from doc comment)
144    pub brief: Option<String>,
145    /// Full docstring
146    pub docstring: Option<String>,
147    /// Starting line (1-indexed)
148    pub line_start: u32,
149    /// Ending line (1-indexed)
150    pub line_end: u32,
151    /// Starting column (0-indexed)
152    pub col_start: u32,
153    /// Ending column (0-indexed)
154    pub col_end: u32,
155    /// Parent symbol ID (e.g., impl block for methods)
156    pub parent_id: Option<String>,
157    /// Source code of just this symbol
158    pub source: Option<String>,
159}
160
161impl Symbol {
162    /// Create a unique ID for this symbol.
163    pub fn make_id(file_path: &str, name: &str, parent: Option<&str>) -> String {
164        match parent {
165            Some(p) => format!("{}::{}::{}", file_path, p, name),
166            None => format!("{}::{}", file_path, name),
167        }
168    }
169
170    /// Create a unique ID for this symbol with line number for disambiguation.
171    pub fn make_id_with_line(
172        file_path: &str,
173        name: &str,
174        parent: Option<&str>,
175        line: u32,
176    ) -> String {
177        match parent {
178            Some(p) => format!("{}::{}::{}@{}", file_path, p, name, line),
179            None => format!("{}::{}@{}", file_path, name, line),
180        }
181    }
182
183    /// Generate text for embedding (semantic search).
184    pub fn to_embedding_text(&self) -> String {
185        let mut parts = vec![self.name.clone(), self.kind.as_str().to_string()];
186
187        if let Some(ref sig) = self.signature {
188            parts.push(sig.clone());
189        }
190
191        if let Some(ref brief) = self.brief {
192            parts.push(brief.clone());
193        }
194
195        // Add semantic hints based on kind
196        match self.kind {
197            SymbolKind::Function | SymbolKind::Method => {
198                parts.push("function method procedure".into());
199            }
200            SymbolKind::Struct | SymbolKind::Class => {
201                parts.push("struct type data structure class".into());
202            }
203            SymbolKind::Enum => {
204                parts.push("enum enumeration variant".into());
205            }
206            SymbolKind::Trait | SymbolKind::Interface => {
207                parts.push("trait interface contract".into());
208            }
209            _ => {}
210        }
211
212        parts.join(" ")
213    }
214}
215
216/// A stored MinHash fingerprint for a function/method symbol.
217///
218/// See [`crate::fingerprint`] for how fingerprints are computed. The
219/// `minhash` blob is the 128-permutation signature serialized as 1024
220/// little-endian bytes.
221#[derive(Debug, Clone, PartialEq, Eq)]
222pub struct Fingerprint {
223    /// Symbol id (`path::[parent::]name@line`).
224    pub symbol_id: String,
225    /// Index-relative path of the file containing the symbol.
226    pub file_path: String,
227    /// MinHash signature: 128 u64 words, little-endian (1024 bytes).
228    pub minhash: Vec<u8>,
229    /// Number of normalized tokens in the symbol's token stream.
230    pub token_count: i64,
231}
232
233/// The kind of relationship between symbols.
234#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
235#[serde(rename_all = "snake_case")]
236pub enum EdgeKind {
237    Calls,
238    Imports,
239    Uses,
240    Extends,
241    Implements,
242    Returns,
243    Parameter,
244    Field,
245    Contains,
246}
247
248impl EdgeKind {
249    pub fn as_str(&self) -> &'static str {
250        match self {
251            EdgeKind::Calls => "calls",
252            EdgeKind::Imports => "imports",
253            EdgeKind::Uses => "uses",
254            EdgeKind::Extends => "extends",
255            EdgeKind::Implements => "implements",
256            EdgeKind::Returns => "returns",
257            EdgeKind::Parameter => "parameter",
258            EdgeKind::Field => "field",
259            EdgeKind::Contains => "contains",
260        }
261    }
262}
263
264impl FromStr for EdgeKind {
265    type Err = ();
266    fn from_str(s: &str) -> Result<Self, Self::Err> {
267        match s {
268            "calls" => Ok(EdgeKind::Calls),
269            "imports" => Ok(EdgeKind::Imports),
270            "uses" => Ok(EdgeKind::Uses),
271            "extends" => Ok(EdgeKind::Extends),
272            "implements" => Ok(EdgeKind::Implements),
273            "returns" => Ok(EdgeKind::Returns),
274            "parameter" => Ok(EdgeKind::Parameter),
275            "field" => Ok(EdgeKind::Field),
276            "contains" => Ok(EdgeKind::Contains),
277            _ => Err(()),
278        }
279    }
280}
281
282/// A relationship between symbols.
283#[derive(Debug, Clone, Serialize, Deserialize)]
284pub struct Edge {
285    /// Source symbol ID
286    pub source_id: String,
287    /// Target symbol ID (None if unresolved/external)
288    pub target_id: Option<String>,
289    /// Target name (for unresolved references)
290    pub target_name: String,
291    /// Kind of relationship
292    pub kind: EdgeKind,
293    /// Line where the reference occurs
294    pub line: Option<u32>,
295    /// Column where the reference occurs
296    pub col: Option<u32>,
297    /// Brief context snippet
298    pub context: Option<String>,
299}
300
301/// Module-level information.
302#[derive(Debug, Clone, Serialize, Deserialize)]
303pub struct ModuleInfo {
304    pub file_path: String,
305    pub module_name: Option<String>,
306    pub exports: Vec<String>,
307    pub imports: Vec<ImportInfo>,
308}
309
310/// Import information.
311#[derive(Debug, Clone, Serialize, Deserialize)]
312pub struct ImportInfo {
313    pub from: String,
314    pub names: Vec<String>,
315    pub alias: Option<String>,
316}
317
318/// Result of parsing a file.
319#[derive(Debug, Clone)]
320pub struct ParseResult {
321    #[allow(dead_code)]
322    pub file_path: String,
323    pub language: String,
324    pub symbols: Vec<Symbol>,
325    pub edges: Vec<Edge>,
326    pub module: Option<ModuleInfo>,
327}
328
329/// Statistics about the codebase.
330#[derive(Debug, Clone, Serialize, Deserialize)]
331pub struct CodebaseStats {
332    pub files: i64,
333    pub symbols: i64,
334    pub edges: i64,
335    pub functions: i64,
336    pub structs: i64,
337    pub enums: i64,
338    pub traits: i64,
339}