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(hex::encode(hasher.finalize()))
217}
218
219pub fn CreateFileMetadata(
221 path:PathBuf,
222 size:u64,
223 modified:chrono::DateTime<chrono::Utc>,
224 mime_type:String,
225 language:Option<String>,
226 line_count:Option<u32>,
227 checksum:String,
228 is_symlink:bool,
229 permissions:String,
230 encoding:Option<String>,
231 symbol_count:u32,
232) -> FileMetadata {
233 FileMetadata {
234 path,
235 size,
236 modified,
237 mime_type,
238 language,
239 line_count,
240 checksum,
241 is_symlink,
242 permissions,
243 encoding,
244 indexed_at:chrono::Utc::now(),
245 symbol_count,
246 }
247}
248
249pub fn CreateSymbolInfo(name:String, kind:SymbolKind, line:u32, column:u32, full_path:String) -> SymbolInfo {
251 SymbolInfo { name, kind, line, column, full_path }
252}
253
254pub fn CreateSymbolLocation(file_path:PathBuf, line:u32, symbol:SymbolInfo) -> SymbolLocation {
256 SymbolLocation { file_path, line, symbol }
257}
258
259#[cfg(unix)]
261pub fn GetPermissionsString(metadata:&std::fs::Metadata) -> String {
262 let mode = metadata.permissions().mode();
263 let mut perms = String::new();
264 perms.push(if mode & 0o400 != 0 { 'r' } else { '-' });
266 perms.push(if mode & 0o200 != 0 { 'w' } else { '-' });
268 perms.push(if mode & 0o100 != 0 { 'x' } else { '-' });
270 perms.push(if mode & 0o040 != 0 { 'r' } else { '-' });
272 perms.push(if mode & 0o020 != 0 { 'w' } else { '-' });
273 perms.push(if mode & 0o010 != 0 { 'x' } else { '-' });
274 perms.push(if mode & 0o004 != 0 { 'r' } else { '-' });
276 perms.push(if mode & 0o002 != 0 { 'w' } else { '-' });
277 perms.push(if mode & 0o001 != 0 { 'x' } else { '-' });
278 perms
279}
280
281#[cfg(not(unix))]
283pub fn GetPermissionsString(_metadata:&std::fs::Metadata) -> String { "--------".to_string() }
284
285pub fn ValidateFileSize(size:u64) -> Result<()> {
287 if size > MAX_FILE_SIZE_BYTES {
288 return Err(AirError::FileSystem(format!(
289 "File size {} exceeds maximum allowed size of {} bytes",
290 size, MAX_FILE_SIZE_BYTES
291 )));
292 }
293 Ok(())
294}
295
296pub fn ValidateIndexSize(index:&FileIndex) -> Result<()> {
298 const MAX_INDEXED_FILES:usize = 1_000_000;
299 const MAX_SYMBOLS:usize = 10_000_000;
300
301 if index.files.len() > MAX_INDEXED_FILES {
302 return Err(AirError::Internal(format!(
303 "Index exceeds maximum file count: {} > {}",
304 index.files.len(),
305 MAX_INDEXED_FILES
306 )));
307 }
308
309 let total_symbols:usize = index.file_symbols.values().map(|v| v.len()).sum();
310 if total_symbols > MAX_SYMBOLS {
311 return Err(AirError::Internal(format!(
312 "Index exceeds maximum symbol count: {} > {}",
313 total_symbols, MAX_SYMBOLS
314 )));
315 }
316
317 Ok(())
318}