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 pub kind:SymbolKind,
86 pub line:u32,
88 pub column:u32,
90 pub full_path:String,
92}
93
94#[derive(Debug, Clone, Serialize, Deserialize, Hash, Eq, PartialEq)]
96pub enum SymbolKind {
97 File = 0,
98 Module = 1,
99 Namespace = 2,
100 Package = 3,
101 Class = 4,
102 Method = 5,
103 Property = 6,
104 Field = 7,
105 Constructor = 8,
106 Enum = 9,
107 Interface = 10,
108 Function = 11,
109 Variable = 12,
110 Constant = 13,
111 String = 14,
112 Number = 15,
113 Boolean = 16,
114 Array = 17,
115 Object = 18,
116 Key = 19,
117 Null = 20,
118 EnumMember = 21,
119 Struct = 22,
120 Event = 23,
121 Operator = 24,
122 TypeParameter = 25,
123}
124
125#[derive(Debug, Clone, Serialize, Deserialize)]
127pub struct SymbolLocation {
128 pub file_path:PathBuf,
130 pub line:u32,
132 pub symbol:SymbolInfo,
134}
135
136#[derive(Debug, Clone, Serialize, Deserialize)]
138pub struct FileMetadata {
139 pub path:PathBuf,
141 pub size:u64,
143 pub modified:chrono::DateTime<chrono::Utc>,
145 pub mime_type:String,
147 pub language:Option<String>,
149 pub line_count:Option<u32>,
151 pub checksum:String,
153 pub is_symlink:bool,
155 pub permissions:String,
157 pub encoding:Option<String>,
159 pub indexed_at:chrono::DateTime<chrono::Utc>,
161 pub symbol_count:u32,
163}
164
165#[derive(Debug, Clone, Serialize, Deserialize)]
167pub struct FileIndex {
168 pub files:HashMap<PathBuf, FileMetadata>,
170 pub content_index:HashMap<String, Vec<PathBuf>>,
173 pub symbol_index:HashMap<String, Vec<SymbolLocation>>,
176 pub file_symbols:HashMap<PathBuf, Vec<SymbolInfo>>,
178 pub last_updated:chrono::DateTime<chrono::Utc>,
180 pub index_version:String,
182 pub index_checksum:String,
184}
185
186pub fn CreateNewIndex() -> FileIndex {
188 FileIndex {
189 files:HashMap::new(),
190 content_index:HashMap::new(),
191 symbol_index:HashMap::new(),
192 file_symbols:HashMap::new(),
193 last_updated:chrono::Utc::now(),
194 index_version:GenerateIndexVersion(),
195 index_checksum:String::new(),
196 }
197}
198
199pub fn GenerateIndexVersion() -> String { format!("{}-{}", env!("CARGO_PKG_VERSION"), chrono::Utc::now().timestamp()) }
201
202pub fn CalculateIndexChecksum(index:&FileIndex) -> Result<String> {
204 let checksum_input = format!(
205 "{}:{}:{}:{}",
206 index.files.len(),
207 index.content_index.len(),
208 index.symbol_index.len(),
209 index.last_updated.timestamp()
210 );
211
212 let mut hasher = Sha256::new();
213 hasher.update(checksum_input.as_bytes());
214 Ok(format!("{:x}", hasher.finalize()))
215}
216
217pub fn CreateFileMetadata(
219 path:PathBuf,
220 size:u64,
221 modified:chrono::DateTime<chrono::Utc>,
222 mime_type:String,
223 language:Option<String>,
224 line_count:Option<u32>,
225 checksum:String,
226 is_symlink:bool,
227 permissions:String,
228 encoding:Option<String>,
229 symbol_count:u32,
230) -> FileMetadata {
231 FileMetadata {
232 path,
233 size,
234 modified,
235 mime_type,
236 language,
237 line_count,
238 checksum,
239 is_symlink,
240 permissions,
241 encoding,
242 indexed_at:chrono::Utc::now(),
243 symbol_count,
244 }
245}
246
247pub fn CreateSymbolInfo(name:String, kind:SymbolKind, line:u32, column:u32, full_path:String) -> SymbolInfo {
249 SymbolInfo { name, kind, line, column, full_path }
250}
251
252pub fn CreateSymbolLocation(file_path:PathBuf, line:u32, symbol:SymbolInfo) -> SymbolLocation {
254 SymbolLocation { file_path, line, symbol }
255}
256
257#[cfg(unix)]
259pub fn GetPermissionsString(metadata:&std::fs::Metadata) -> String {
260 let mode = metadata.permissions().mode();
261 let mut perms = String::new();
262 perms.push(if mode & 0o400 != 0 { 'r' } else { '-' });
264 perms.push(if mode & 0o200 != 0 { 'w' } else { '-' });
266 perms.push(if mode & 0o100 != 0 { 'x' } else { '-' });
268 perms.push(if mode & 0o040 != 0 { 'r' } else { '-' });
270 perms.push(if mode & 0o020 != 0 { 'w' } else { '-' });
271 perms.push(if mode & 0o010 != 0 { 'x' } else { '-' });
272 perms.push(if mode & 0o004 != 0 { 'r' } else { '-' });
274 perms.push(if mode & 0o002 != 0 { 'w' } else { '-' });
275 perms.push(if mode & 0o001 != 0 { 'x' } else { '-' });
276 perms
277}
278
279#[cfg(not(unix))]
281pub fn GetPermissionsString(_metadata:&std::fs::Metadata) -> String { "--------".to_string() }
282
283pub fn ValidateFileSize(size:u64) -> Result<()> {
285 if size > MAX_FILE_SIZE_BYTES {
286 return Err(AirError::FileSystem(format!(
287 "File size {} exceeds maximum allowed size of {} bytes",
288 size, MAX_FILE_SIZE_BYTES
289 )));
290 }
291 Ok(())
292}
293
294pub fn ValidateIndexSize(index:&FileIndex) -> Result<()> {
296 const MAX_INDEXED_FILES:usize = 1_000_000;
297 const MAX_SYMBOLS:usize = 10_000_000;
298
299 if index.files.len() > MAX_INDEXED_FILES {
300 return Err(AirError::Internal(format!(
301 "Index exceeds maximum file count: {} > {}",
302 index.files.len(),
303 MAX_INDEXED_FILES
304 )));
305 }
306
307 let total_symbols:usize = index.file_symbols.values().map(|v| v.len()).sum();
308 if total_symbols > MAX_SYMBOLS {
309 return Err(AirError::Internal(format!(
310 "Index exceeds maximum symbol count: {} > {}",
311 total_symbols, MAX_SYMBOLS
312 )));
313 }
314
315 Ok(())
316}