AirLibrary/Indexing/State/
CreateState.rs1use std::{collections::HashMap, path::PathBuf};
68#[cfg(unix)]
69use std::os::unix::fs::PermissionsExt;
70
71use serde::{Deserialize, Serialize};
72use sha2::{Digest, Sha256};
73
74use crate::{AirError, Result};
75
76pub const MAX_FILE_SIZE_BYTES:u64 = 100 * 1024 * 1024;
78
79#[derive(Debug, Clone, Serialize, Deserialize)]
81pub struct SymbolInfo {
82 pub name:String,
84
85 pub kind:SymbolKind,
87
88 pub line:u32,
90
91 pub column:u32,
93
94 pub full_path:String,
96}
97
98#[derive(Debug, Clone, Serialize, Deserialize, Hash, Eq, PartialEq)]
100pub enum SymbolKind {
101 File = 0,
102
103 Module = 1,
104
105 Namespace = 2,
106
107 Package = 3,
108
109 Class = 4,
110
111 Method = 5,
112
113 Property = 6,
114
115 Field = 7,
116
117 Constructor = 8,
118
119 Enum = 9,
120
121 Interface = 10,
122
123 Function = 11,
124
125 Variable = 12,
126
127 Constant = 13,
128
129 String = 14,
130
131 Number = 15,
132
133 Boolean = 16,
134
135 Array = 17,
136
137 Object = 18,
138
139 Key = 19,
140
141 Null = 20,
142
143 EnumMember = 21,
144
145 Struct = 22,
146
147 Event = 23,
148
149 Operator = 24,
150
151 TypeParameter = 25,
152}
153
154#[derive(Debug, Clone, Serialize, Deserialize)]
156pub struct SymbolLocation {
157 pub file_path:PathBuf,
159
160 pub line:u32,
162
163 pub symbol:SymbolInfo,
165}
166
167#[derive(Debug, Clone, Serialize, Deserialize)]
169pub struct FileMetadata {
170 pub path:PathBuf,
172
173 pub size:u64,
175
176 pub modified:chrono::DateTime<chrono::Utc>,
178
179 pub mime_type:String,
181
182 pub language:Option<String>,
184
185 pub line_count:Option<u32>,
187
188 pub checksum:String,
190
191 pub is_symlink:bool,
193
194 pub permissions:String,
196
197 pub encoding:Option<String>,
199
200 pub indexed_at:chrono::DateTime<chrono::Utc>,
202
203 pub symbol_count:u32,
205}
206
207#[derive(Debug, Clone, Serialize, Deserialize)]
209pub struct FileIndex {
210 pub files:HashMap<PathBuf, FileMetadata>,
212
213 pub content_index:HashMap<String, Vec<PathBuf>>,
216
217 pub symbol_index:HashMap<String, Vec<SymbolLocation>>,
220
221 pub file_symbols:HashMap<PathBuf, Vec<SymbolInfo>>,
223
224 pub last_updated:chrono::DateTime<chrono::Utc>,
226
227 pub index_version:String,
229
230 pub index_checksum:String,
232}
233
234pub fn CreateNewIndex() -> FileIndex {
236 FileIndex {
237 files:HashMap::new(),
238
239 content_index:HashMap::new(),
240
241 symbol_index:HashMap::new(),
242
243 file_symbols:HashMap::new(),
244
245 last_updated:chrono::Utc::now(),
246
247 index_version:GenerateIndexVersion(),
248
249 index_checksum:String::new(),
250 }
251}
252
253pub fn GenerateIndexVersion() -> String { format!("{}-{}", env!("CARGO_PKG_VERSION"), chrono::Utc::now().timestamp()) }
255
256pub fn CalculateIndexChecksum(index:&FileIndex) -> Result<String> {
258 let checksum_input = format!(
259 "{}:{}:{}:{}",
260 index.files.len(),
261 index.content_index.len(),
262 index.symbol_index.len(),
263 index.last_updated.timestamp()
264 );
265
266 let mut hasher = Sha256::new();
267
268 hasher.update(checksum_input.as_bytes());
269
270 Ok(hex::encode(hasher.finalize()))
273}
274
275pub fn CreateFileMetadata(
277 path:PathBuf,
278
279 size:u64,
280
281 modified:chrono::DateTime<chrono::Utc>,
282
283 mime_type:String,
284
285 language:Option<String>,
286
287 line_count:Option<u32>,
288
289 checksum:String,
290
291 is_symlink:bool,
292
293 permissions:String,
294
295 encoding:Option<String>,
296
297 symbol_count:u32,
298) -> FileMetadata {
299 FileMetadata {
300 path,
301
302 size,
303
304 modified,
305
306 mime_type,
307
308 language,
309
310 line_count,
311
312 checksum,
313
314 is_symlink,
315
316 permissions,
317
318 encoding,
319
320 indexed_at:chrono::Utc::now(),
321
322 symbol_count,
323 }
324}
325
326pub fn CreateSymbolInfo(name:String, kind:SymbolKind, line:u32, column:u32, full_path:String) -> SymbolInfo {
328 SymbolInfo { name, kind, line, column, full_path }
329}
330
331pub fn CreateSymbolLocation(file_path:PathBuf, line:u32, symbol:SymbolInfo) -> SymbolLocation {
333 SymbolLocation { file_path, line, symbol }
334}
335
336#[cfg(unix)]
338pub fn GetPermissionsString(metadata:&std::fs::Metadata) -> String {
339 let mode = metadata.permissions().mode();
340
341 let mut perms = String::new();
342
343 perms.push(if mode & 0o400 != 0 { 'r' } else { '-' });
345
346 perms.push(if mode & 0o200 != 0 { 'w' } else { '-' });
348
349 perms.push(if mode & 0o100 != 0 { 'x' } else { '-' });
351
352 perms.push(if mode & 0o040 != 0 { 'r' } else { '-' });
354
355 perms.push(if mode & 0o020 != 0 { 'w' } else { '-' });
356
357 perms.push(if mode & 0o010 != 0 { 'x' } else { '-' });
358
359 perms.push(if mode & 0o004 != 0 { 'r' } else { '-' });
361
362 perms.push(if mode & 0o002 != 0 { 'w' } else { '-' });
363
364 perms.push(if mode & 0o001 != 0 { 'x' } else { '-' });
365
366 perms
367}
368
369#[cfg(not(unix))]
371pub fn GetPermissionsString(_metadata:&std::fs::Metadata) -> String { "--------".to_string() }
372
373pub fn ValidateFileSize(size:u64) -> Result<()> {
375 if size > MAX_FILE_SIZE_BYTES {
376 return Err(AirError::FileSystem(format!(
377 "File size {} exceeds maximum allowed size of {} bytes",
378 size, MAX_FILE_SIZE_BYTES
379 )));
380 }
381
382 Ok(())
383}
384
385pub fn ValidateIndexSize(index:&FileIndex) -> Result<()> {
387 const MAX_INDEXED_FILES:usize = 1_000_000;
388
389 const MAX_SYMBOLS:usize = 10_000_000;
390
391 if index.files.len() > MAX_INDEXED_FILES {
392 return Err(AirError::Internal(format!(
393 "Index exceeds maximum file count: {} > {}",
394 index.files.len(),
395 MAX_INDEXED_FILES
396 )));
397 }
398
399 let total_symbols:usize = index.file_symbols.values().map(|v| v.len()).sum();
400
401 if total_symbols > MAX_SYMBOLS {
402 return Err(AirError::Internal(format!(
403 "Index exceeds maximum symbol count: {} > {}",
404 total_symbols, MAX_SYMBOLS
405 )));
406 }
407
408 Ok(())
409}