1use crate::db::IndexDb;
2use serde::{Deserialize, Serialize};
3
4#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
5#[serde(rename_all = "snake_case")]
6pub enum SymbolKind {
7 File,
8 Class,
9 Interface,
10 Enum,
11 Module,
12 Method,
13 Function,
14 Property,
15 Variable,
16 TypeAlias,
17 Unknown,
18}
19
20impl SymbolKind {
21 pub fn as_label(&self) -> &'static str {
22 match self {
23 SymbolKind::File => "file",
24 SymbolKind::Class => "class",
25 SymbolKind::Interface => "interface",
26 SymbolKind::Enum => "enum",
27 SymbolKind::Module => "module",
28 SymbolKind::Method => "method",
29 SymbolKind::Function => "function",
30 SymbolKind::Property => "property",
31 SymbolKind::Variable => "variable",
32 SymbolKind::TypeAlias => "type_alias",
33 SymbolKind::Unknown => "unknown",
34 }
35 }
36
37 pub fn from_str_label(s: &str) -> SymbolKind {
38 match s {
39 "class" => SymbolKind::Class,
40 "interface" => SymbolKind::Interface,
41 "enum" => SymbolKind::Enum,
42 "module" => SymbolKind::Module,
43 "method" => SymbolKind::Method,
44 "function" => SymbolKind::Function,
45 "property" => SymbolKind::Property,
46 "variable" => SymbolKind::Variable,
47 "type_alias" => SymbolKind::TypeAlias,
48 _ => SymbolKind::Unknown,
49 }
50 }
51}
52
53#[derive(Debug, Clone, Serialize)]
54pub struct SymbolInfo {
55 pub name: String,
56 pub kind: SymbolKind,
57 pub file_path: String,
58 pub line: usize,
59 pub column: usize,
60 pub signature: String,
61 pub name_path: String,
62 pub id: String,
63 #[serde(skip_serializing_if = "Option::is_none")]
64 pub body: Option<String>,
65 #[serde(default, skip_serializing_if = "Vec::is_empty")]
66 pub children: Vec<SymbolInfo>,
67 #[serde(skip)]
70 pub start_byte: u32,
71 #[serde(skip)]
72 pub end_byte: u32,
73}
74
75pub fn make_symbol_id(file_path: &str, kind: &SymbolKind, name_path: &str) -> String {
81 let label = kind.as_label();
82 let mut id = String::with_capacity(file_path.len() + 1 + label.len() + 1 + name_path.len());
83 id.push_str(file_path);
84 id.push('#');
85 id.push_str(label);
86 id.push(':');
87 id.push_str(name_path);
88 id
89}
90
91pub fn parse_symbol_id(input: &str) -> Option<(&str, &str, &str)> {
93 let hash_pos = input.find('#')?;
94 let after_hash = &input[hash_pos + 1..];
95 let colon_pos = after_hash.find(':')?;
96 let file_path = &input[..hash_pos];
97 let kind = &after_hash[..colon_pos];
98 let name_path = &after_hash[colon_pos + 1..];
99 if file_path.is_empty() || kind.is_empty() || name_path.is_empty() {
100 return None;
101 }
102 Some((file_path, kind, name_path))
103}
104
105#[derive(Debug, Clone, Serialize)]
106pub struct IndexStats {
107 pub indexed_files: usize,
108 pub supported_files: usize,
109 pub stale_files: usize,
110}
111
112#[derive(Debug, Clone, Serialize)]
113pub struct RankedContextEntry {
114 pub name: String,
115 pub kind: String,
116 pub file: String,
117 pub line: usize,
118 pub signature: String,
119 #[serde(skip_serializing_if = "Option::is_none")]
120 pub body: Option<String>,
121 pub relevance_score: i32,
122}
123
124#[derive(Debug, Clone, Serialize)]
125pub struct RankedContextResult {
126 pub query: String,
127 pub symbols: Vec<RankedContextEntry>,
128 pub count: usize,
129 pub token_budget: usize,
130 pub chars_used: usize,
131}
132
133#[derive(Debug, Clone, Serialize, Deserialize)]
134pub(crate) struct ParsedSymbol {
135 pub name: String,
136 pub kind: SymbolKind,
137 pub file_path: String,
138 pub line: usize,
139 pub column: usize,
140 pub start_byte: u32,
141 pub end_byte: u32,
142 pub signature: String,
143 pub body: Option<String>,
144 pub name_path: String,
145 pub children: Vec<ParsedSymbol>,
146}
147
148pub(crate) enum ReadDb<'a> {
150 Owned(IndexDb),
151 Writer(std::sync::MutexGuard<'a, IndexDb>),
152}
153
154pub(crate) struct AnalyzedFile {
160 pub relative_path: String,
161 pub mtime: i64,
162 pub content_hash: String,
163 pub size_bytes: i64,
164 pub language_ext: String,
165 pub symbols: Vec<ParsedSymbol>,
166 pub imports: Vec<crate::db::NewImport>,
167 pub calls: Vec<crate::db::NewCall>,
168}
169
170impl std::ops::Deref for ReadDb<'_> {
171 type Target = IndexDb;
172 fn deref(&self) -> &IndexDb {
173 match self {
174 ReadDb::Owned(db) => db,
175 ReadDb::Writer(guard) => guard,
176 }
177 }
178}