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/// The kind of relationship between symbols.
217#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
218#[serde(rename_all = "snake_case")]
219pub enum EdgeKind {
220    Calls,
221    Imports,
222    Uses,
223    Extends,
224    Implements,
225    Returns,
226    Parameter,
227    Field,
228    Contains,
229}
230
231impl EdgeKind {
232    pub fn as_str(&self) -> &'static str {
233        match self {
234            EdgeKind::Calls => "calls",
235            EdgeKind::Imports => "imports",
236            EdgeKind::Uses => "uses",
237            EdgeKind::Extends => "extends",
238            EdgeKind::Implements => "implements",
239            EdgeKind::Returns => "returns",
240            EdgeKind::Parameter => "parameter",
241            EdgeKind::Field => "field",
242            EdgeKind::Contains => "contains",
243        }
244    }
245}
246
247impl FromStr for EdgeKind {
248    type Err = ();
249    fn from_str(s: &str) -> Result<Self, Self::Err> {
250        match s {
251            "calls" => Ok(EdgeKind::Calls),
252            "imports" => Ok(EdgeKind::Imports),
253            "uses" => Ok(EdgeKind::Uses),
254            "extends" => Ok(EdgeKind::Extends),
255            "implements" => Ok(EdgeKind::Implements),
256            "returns" => Ok(EdgeKind::Returns),
257            "parameter" => Ok(EdgeKind::Parameter),
258            "field" => Ok(EdgeKind::Field),
259            "contains" => Ok(EdgeKind::Contains),
260            _ => Err(()),
261        }
262    }
263}
264
265/// A relationship between symbols.
266#[derive(Debug, Clone, Serialize, Deserialize)]
267pub struct Edge {
268    /// Source symbol ID
269    pub source_id: String,
270    /// Target symbol ID (None if unresolved/external)
271    pub target_id: Option<String>,
272    /// Target name (for unresolved references)
273    pub target_name: String,
274    /// Kind of relationship
275    pub kind: EdgeKind,
276    /// Line where the reference occurs
277    pub line: Option<u32>,
278    /// Column where the reference occurs
279    pub col: Option<u32>,
280    /// Brief context snippet
281    pub context: Option<String>,
282}
283
284/// Module-level information.
285#[derive(Debug, Clone, Serialize, Deserialize)]
286pub struct ModuleInfo {
287    pub file_path: String,
288    pub module_name: Option<String>,
289    pub exports: Vec<String>,
290    pub imports: Vec<ImportInfo>,
291}
292
293/// Import information.
294#[derive(Debug, Clone, Serialize, Deserialize)]
295pub struct ImportInfo {
296    pub from: String,
297    pub names: Vec<String>,
298    pub alias: Option<String>,
299}
300
301/// Result of parsing a file.
302#[derive(Debug, Clone)]
303pub struct ParseResult {
304    #[allow(dead_code)]
305    pub file_path: String,
306    pub language: String,
307    pub symbols: Vec<Symbol>,
308    pub edges: Vec<Edge>,
309    pub module: Option<ModuleInfo>,
310}
311
312/// Statistics about the codebase.
313#[derive(Debug, Clone, Serialize, Deserialize)]
314pub struct CodebaseStats {
315    pub files: i64,
316    pub symbols: i64,
317    pub edges: i64,
318    pub functions: i64,
319    pub structs: i64,
320    pub enums: i64,
321    pub traits: i64,
322}