AirLibrary/Indexing/State/
CreateState.rs1use std::{
68 collections::{HashMap, HashSet},
69 path::PathBuf,
70};
71#[cfg(unix)]
72use std::os::unix::fs::PermissionsExt;
73
74use serde::{Deserialize, Serialize};
75use sha2::{Digest, Sha256};
76
77use crate::{AirError, Result};
78
79pub const MAX_FILE_SIZE_BYTES:u64 = 100 * 1024 * 1024;
81
82#[derive(Debug, Clone, Serialize, Deserialize)]
84pub struct SymbolInfo {
85 pub name:String,
87 pub kind:SymbolKind,
89 pub line:u32,
91 pub column:u32,
93 pub full_path:String,
95}
96
97#[derive(Debug, Clone, Serialize, Deserialize, Hash, Eq, PartialEq)]
99pub enum SymbolKind {
100 File = 0,
101 Module = 1,
102 Namespace = 2,
103 Package = 3,
104 Class = 4,
105 Method = 5,
106 Property = 6,
107 Field = 7,
108 Constructor = 8,
109 Enum = 9,
110 Interface = 10,
111 Function = 11,
112 Variable = 12,
113 Constant = 13,
114 String = 14,
115 Number = 15,
116 Boolean = 16,
117 Array = 17,
118 Object = 18,
119 Key = 19,
120 Null = 20,
121 EnumMember = 21,
122 Struct = 22,
123 Event = 23,
124 Operator = 24,
125 TypeParameter = 25,
126}
127
128#[derive(Debug, Clone, Serialize, Deserialize)]
130pub struct SymbolLocation {
131 pub file_path:PathBuf,
133 pub line:u32,
135 pub symbol:SymbolInfo,
137}
138
139#[derive(Debug, Clone, Serialize, Deserialize)]
141pub struct FileMetadata {
142 pub path:PathBuf,
144 pub size:u64,
146 pub modified:chrono::DateTime<chrono::Utc>,
148 pub mime_type:String,
150 pub language:Option<String>,
152 pub line_count:Option<u32>,
154 pub checksum:String,
156 pub is_symlink:bool,
158 pub permissions:String,
160 pub encoding:Option<String>,
162 pub indexed_at:chrono::DateTime<chrono::Utc>,
164 pub symbol_count:u32,
166}
167
168#[derive(Debug, Clone, Serialize, Deserialize)]
170pub struct FileIndex {
171 pub files:HashMap<PathBuf, FileMetadata>,
173 pub content_index:HashMap<String, Vec<PathBuf>>,
176 pub symbol_index:HashMap<String, Vec<SymbolLocation>>,
179 pub file_symbols:HashMap<PathBuf, Vec<SymbolInfo>>,
181 pub last_updated:chrono::DateTime<chrono::Utc>,
183 pub index_version:String,
185 pub index_checksum:String,
187}
188
189pub fn CreateNewIndex() -> FileIndex {
191 FileIndex {
192 files:HashMap::new(),
193 content_index:HashMap::new(),
194 symbol_index:HashMap::new(),
195 file_symbols:HashMap::new(),
196 last_updated:chrono::Utc::now(),
197 index_version:GenerateIndexVersion(),
198 index_checksum:String::new(),
199 }
200}
201
202pub fn GenerateIndexVersion() -> String { format!("{}-{}", env!("CARGO_PKG_VERSION"), chrono::Utc::now().timestamp()) }
204
205pub fn CalculateIndexChecksum(index:&FileIndex) -> Result<String> {
207 let checksum_input = format!(
208 "{}:{}:{}:{}",
209 index.files.len(),
210 index.content_index.len(),
211 index.symbol_index.len(),
212 index.last_updated.timestamp()
213 );
214
215 let mut hasher = Sha256::new();
216 hasher.update(checksum_input.as_bytes());
217 Ok(format!("{:x}", hasher.finalize()))
218}
219
220pub fn CreateFileMetadata(
222 path:PathBuf,
223 size:u64,
224 modified:chrono::DateTime<chrono::Utc>,
225 mime_type:String,
226 language:Option<String>,
227 line_count:Option<u32>,
228 checksum:String,
229 is_symlink:bool,
230 permissions:String,
231 encoding:Option<String>,
232 symbol_count:u32,
233) -> FileMetadata {
234 FileMetadata {
235 path,
236 size,
237 modified,
238 mime_type,
239 language,
240 line_count,
241 checksum,
242 is_symlink,
243 permissions,
244 encoding,
245 indexed_at:chrono::Utc::now(),
246 symbol_count,
247 }
248}
249
250pub fn CreateSymbolInfo(name:String, kind:SymbolKind, line:u32, column:u32, full_path:String) -> SymbolInfo {
252 SymbolInfo { name, kind, line, column, full_path }
253}
254
255pub fn CreateSymbolLocation(file_path:PathBuf, line:u32, symbol:SymbolInfo) -> SymbolLocation {
257 SymbolLocation { file_path, line, symbol }
258}
259
260#[cfg(unix)]
262pub fn GetPermissionsString(metadata:&std::fs::Metadata) -> String {
263 let mode = metadata.permissions().mode();
264 let mut perms = String::new();
265 perms.push(if mode & 0o400 != 0 { 'r' } else { '-' });
267 perms.push(if mode & 0o200 != 0 { 'w' } else { '-' });
269 perms.push(if mode & 0o100 != 0 { 'x' } else { '-' });
271 perms.push(if mode & 0o040 != 0 { 'r' } else { '-' });
273 perms.push(if mode & 0o020 != 0 { 'w' } else { '-' });
274 perms.push(if mode & 0o010 != 0 { 'x' } else { '-' });
275 perms.push(if mode & 0o004 != 0 { 'r' } else { '-' });
277 perms.push(if mode & 0o002 != 0 { 'w' } else { '-' });
278 perms.push(if mode & 0o001 != 0 { 'x' } else { '-' });
279 perms
280}
281
282#[cfg(not(unix))]
284pub fn GetPermissionsString(_metadata:&std::fs::Metadata) -> String { "--------".to_string() }
285
286pub fn ValidateFileSize(size:u64) -> Result<()> {
288 if size > MAX_FILE_SIZE_BYTES {
289 return Err(AirError::FileSystem(format!(
290 "File size {} exceeds maximum allowed size of {} bytes",
291 size, MAX_FILE_SIZE_BYTES
292 )));
293 }
294 Ok(())
295}
296
297pub fn ValidateIndexSize(index:&FileIndex) -> Result<()> {
299 const MAX_INDEXED_FILES:usize = 1_000_000;
300 const MAX_SYMBOLS:usize = 10_000_000;
301
302 if index.files.len() > MAX_INDEXED_FILES {
303 return Err(AirError::Internal(format!(
304 "Index exceeds maximum file count: {} > {}",
305 index.files.len(),
306 MAX_INDEXED_FILES
307 )));
308 }
309
310 let total_symbols:usize = index.file_symbols.values().map(|v| v.len()).sum();
311 if total_symbols > MAX_SYMBOLS {
312 return Err(AirError::Internal(format!(
313 "Index exceeds maximum symbol count: {} > {}",
314 total_symbols, MAX_SYMBOLS
315 )));
316 }
317
318 Ok(())
319}