1use std::collections::{HashMap, HashSet};
4use std::sync::Arc;
5use std::time::SystemTime;
6
7use async_trait::async_trait;
8use tokio::sync::RwLock;
9
10use crate::error::{VfsError, VfsResult};
11
12#[derive(Debug, Clone)]
14pub struct Metadata {
15 pub is_dir: bool,
17 pub size: u64,
19 pub created: SystemTime,
21 pub modified: SystemTime,
23 pub accessed: SystemTime,
25}
26
27impl Default for Metadata {
28 fn default() -> Self {
29 let now = SystemTime::now();
30 Self {
31 is_dir: false,
32 size: 0,
33 created: now,
34 modified: now,
35 accessed: now,
36 }
37 }
38}
39
40#[derive(Debug, Clone)]
42pub struct DirEntry {
43 pub name: String,
45 pub metadata: Metadata,
47}
48
49#[async_trait]
54pub trait VfsStorage: Send + Sync {
55 async fn read(&self, path: &str) -> VfsResult<Vec<u8>>;
57
58 async fn read_at(&self, path: &str, offset: u64, len: u64) -> VfsResult<Vec<u8>>;
60
61 async fn write(&self, path: &str, data: &[u8]) -> VfsResult<()>;
63
64 async fn write_at(&self, path: &str, offset: u64, data: &[u8]) -> VfsResult<()>;
66
67 async fn set_size(&self, path: &str, size: u64) -> VfsResult<()>;
69
70 async fn delete(&self, path: &str) -> VfsResult<()>;
72
73 async fn exists(&self, path: &str) -> VfsResult<bool>;
75
76 async fn list(&self, path: &str) -> VfsResult<Vec<DirEntry>>;
78
79 async fn stat(&self, path: &str) -> VfsResult<Metadata>;
81
82 async fn mkdir(&self, path: &str) -> VfsResult<()>;
84
85 async fn rmdir(&self, path: &str) -> VfsResult<()>;
87
88 async fn rename(&self, from: &str, to: &str) -> VfsResult<()>;
90
91 fn mkdir_sync(&self, _path: &str) -> VfsResult<()> {
97 Err(VfsError::Storage(
98 "mkdir_sync not implemented for this storage backend".to_string(),
99 ))
100 }
101}
102
103#[derive(Clone)]
120pub struct ArcStorage(Arc<dyn VfsStorage>);
121
122impl ArcStorage {
123 pub fn new(storage: Arc<dyn VfsStorage>) -> Self {
125 Self(storage)
126 }
127}
128
129impl std::fmt::Debug for ArcStorage {
130 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
131 f.debug_tuple("ArcStorage")
132 .field(&"<dyn VfsStorage>")
133 .finish()
134 }
135}
136
137#[async_trait]
138impl VfsStorage for ArcStorage {
139 async fn read(&self, path: &str) -> VfsResult<Vec<u8>> {
140 self.0.read(path).await
141 }
142
143 async fn read_at(&self, path: &str, offset: u64, len: u64) -> VfsResult<Vec<u8>> {
144 self.0.read_at(path, offset, len).await
145 }
146
147 async fn write(&self, path: &str, data: &[u8]) -> VfsResult<()> {
148 self.0.write(path, data).await
149 }
150
151 async fn write_at(&self, path: &str, offset: u64, data: &[u8]) -> VfsResult<()> {
152 self.0.write_at(path, offset, data).await
153 }
154
155 async fn set_size(&self, path: &str, size: u64) -> VfsResult<()> {
156 self.0.set_size(path, size).await
157 }
158
159 async fn delete(&self, path: &str) -> VfsResult<()> {
160 self.0.delete(path).await
161 }
162
163 async fn exists(&self, path: &str) -> VfsResult<bool> {
164 self.0.exists(path).await
165 }
166
167 async fn list(&self, path: &str) -> VfsResult<Vec<DirEntry>> {
168 self.0.list(path).await
169 }
170
171 async fn stat(&self, path: &str) -> VfsResult<Metadata> {
172 self.0.stat(path).await
173 }
174
175 async fn mkdir(&self, path: &str) -> VfsResult<()> {
176 self.0.mkdir(path).await
177 }
178
179 async fn rmdir(&self, path: &str) -> VfsResult<()> {
180 self.0.rmdir(path).await
181 }
182
183 async fn rename(&self, from: &str, to: &str) -> VfsResult<()> {
184 self.0.rename(from, to).await
185 }
186
187 fn mkdir_sync(&self, path: &str) -> VfsResult<()> {
188 self.0.mkdir_sync(path)
189 }
190}
191
192#[derive(Debug, Default)]
198struct StorageState {
199 files: HashMap<String, FileData>,
201 directories: HashSet<String>,
203}
204
205#[derive(Debug)]
210pub struct InMemoryStorage {
211 state: RwLock<StorageState>,
213}
214
215#[derive(Debug, Clone)]
216struct FileData {
217 content: Vec<u8>,
218 created: SystemTime,
219 modified: SystemTime,
220 accessed: SystemTime,
221}
222
223impl Default for InMemoryStorage {
224 fn default() -> Self {
225 Self::new()
226 }
227}
228
229impl InMemoryStorage {
230 #[must_use]
232 pub fn new() -> Self {
233 let mut directories = HashSet::new();
234 directories.insert("/".to_string());
235 Self {
236 state: RwLock::new(StorageState {
237 files: HashMap::new(),
238 directories,
239 }),
240 }
241 }
242
243 fn normalize_path(path: &str) -> VfsResult<String> {
245 if !path.starts_with('/') {
246 return Err(VfsError::InvalidPath(format!(
247 "path must be absolute: {path}"
248 )));
249 }
250
251 let mut components: Vec<&str> = Vec::new();
252 for component in path.split('/') {
253 match component {
254 "" | "." => continue,
255 ".." => {
256 if components.is_empty() {
257 return Err(VfsError::InvalidPath("path escapes root".to_string()));
258 }
259 components.pop();
260 }
261 c => components.push(c),
262 }
263 }
264
265 if components.is_empty() {
266 Ok("/".to_string())
267 } else {
268 Ok(format!("/{}", components.join("/")))
269 }
270 }
271
272 fn parent_path(path: &str) -> Option<String> {
274 if path == "/" {
275 return None;
276 }
277 let normalized = Self::normalize_path(path).ok()?;
278 if normalized == "/" {
279 return None;
280 }
281 match normalized.rfind('/') {
282 Some(0) => Some("/".to_string()),
283 Some(idx) => Some(normalized[..idx].to_string()),
284 None => None,
285 }
286 }
287
288 fn check_parent_exists_with_state(state: &StorageState, path: &str) -> VfsResult<()> {
290 if let Some(parent) = Self::parent_path(path)
291 && !state.directories.contains(&parent)
292 {
293 return Err(VfsError::NotFound(format!("parent directory: {parent}")));
294 }
295 Ok(())
296 }
297}
298
299#[async_trait]
300impl VfsStorage for InMemoryStorage {
301 async fn read(&self, path: &str) -> VfsResult<Vec<u8>> {
302 let path = Self::normalize_path(path)?;
303 let state = self.state.read().await;
304 match state.files.get(&path) {
305 Some(data) => Ok(data.content.clone()),
306 None => {
307 if state.directories.contains(&path) {
308 Err(VfsError::NotFile(path))
309 } else {
310 Err(VfsError::NotFound(path))
311 }
312 }
313 }
314 }
315
316 async fn read_at(&self, path: &str, offset: u64, len: u64) -> VfsResult<Vec<u8>> {
317 let path = Self::normalize_path(path)?;
318 let state = self.state.read().await;
319 match state.files.get(&path) {
320 Some(data) => {
321 let offset = offset as usize;
322 let len = len as usize;
323 if offset >= data.content.len() {
324 Ok(Vec::new())
325 } else {
326 let end = (offset + len).min(data.content.len());
327 Ok(data.content[offset..end].to_vec())
328 }
329 }
330 None => {
331 if state.directories.contains(&path) {
332 Err(VfsError::NotFile(path))
333 } else {
334 Err(VfsError::NotFound(path))
335 }
336 }
337 }
338 }
339
340 async fn write(&self, path: &str, data: &[u8]) -> VfsResult<()> {
341 let path = Self::normalize_path(path)?;
342 let mut state = self.state.write().await;
343
344 Self::check_parent_exists_with_state(&state, &path)?;
345
346 if state.directories.contains(&path) {
348 return Err(VfsError::NotFile(path));
349 }
350
351 let now = SystemTime::now();
352 let file_data = state.files.entry(path).or_insert_with(|| FileData {
353 content: Vec::new(),
354 created: now,
355 modified: now,
356 accessed: now,
357 });
358 file_data.content = data.to_vec();
359 file_data.modified = now;
360 Ok(())
361 }
362
363 async fn write_at(&self, path: &str, offset: u64, data: &[u8]) -> VfsResult<()> {
364 let path = Self::normalize_path(path)?;
365 let mut state = self.state.write().await;
366
367 Self::check_parent_exists_with_state(&state, &path)?;
368
369 if state.directories.contains(&path) {
371 return Err(VfsError::NotFile(path));
372 }
373
374 let now = SystemTime::now();
375 let offset = offset as usize;
376 let file_data = state.files.entry(path).or_insert_with(|| FileData {
377 content: Vec::new(),
378 created: now,
379 modified: now,
380 accessed: now,
381 });
382
383 let needed_len = offset + data.len();
385 if file_data.content.len() < needed_len {
386 file_data.content.resize(needed_len, 0);
387 }
388 file_data.content[offset..offset + data.len()].copy_from_slice(data);
389 file_data.modified = now;
390 Ok(())
391 }
392
393 async fn set_size(&self, path: &str, size: u64) -> VfsResult<()> {
394 let path = Self::normalize_path(path)?;
395 let now = SystemTime::now();
396 let mut state = self.state.write().await;
397 match state.files.get_mut(&path) {
398 Some(data) => {
399 data.content.resize(size as usize, 0);
400 data.modified = now;
401 Ok(())
402 }
403 None => Err(VfsError::NotFound(path)),
404 }
405 }
406
407 async fn delete(&self, path: &str) -> VfsResult<()> {
408 let path = Self::normalize_path(path)?;
409 let mut state = self.state.write().await;
410 if state.files.remove(&path).is_some() {
411 Ok(())
412 } else if state.directories.contains(&path) {
413 Err(VfsError::NotFile(path))
414 } else {
415 Err(VfsError::NotFound(path))
416 }
417 }
418
419 async fn exists(&self, path: &str) -> VfsResult<bool> {
420 let path = Self::normalize_path(path)?;
421 let state = self.state.read().await;
422 Ok(state.files.contains_key(&path) || state.directories.contains(&path))
423 }
424
425 async fn list(&self, path: &str) -> VfsResult<Vec<DirEntry>> {
426 let path = Self::normalize_path(path)?;
427 let state = self.state.read().await;
428
429 if !state.directories.contains(&path) {
431 if state.files.contains_key(&path) {
432 return Err(VfsError::NotDirectory(path));
433 } else {
434 return Err(VfsError::NotFound(path));
435 }
436 }
437
438 let prefix = if path == "/" {
439 "/".to_string()
440 } else {
441 format!("{path}/")
442 };
443
444 let mut entries = Vec::new();
445 let mut seen_names = HashSet::new();
446
447 for (file_path, data) in &state.files {
449 if let Some(rest) = file_path.strip_prefix(&prefix) {
450 if !rest.contains('/') && !rest.is_empty() {
452 seen_names.insert(rest.to_string());
453 entries.push(DirEntry {
454 name: rest.to_string(),
455 metadata: Metadata {
456 is_dir: false,
457 size: data.content.len() as u64,
458 created: data.created,
459 modified: data.modified,
460 accessed: data.accessed,
461 },
462 });
463 }
464 }
465 }
466
467 for dir_path in &state.directories {
469 if let Some(rest) = dir_path.strip_prefix(&prefix) {
470 if !rest.contains('/') && !rest.is_empty() && !seen_names.contains(rest) {
472 let now = SystemTime::now();
473 entries.push(DirEntry {
474 name: rest.to_string(),
475 metadata: Metadata {
476 is_dir: true,
477 size: 0,
478 created: now,
479 modified: now,
480 accessed: now,
481 },
482 });
483 }
484 }
485 }
486
487 entries.sort_by(|a, b| a.name.cmp(&b.name));
488 Ok(entries)
489 }
490
491 async fn stat(&self, path: &str) -> VfsResult<Metadata> {
492 let path = Self::normalize_path(path)?;
493 let state = self.state.read().await;
494
495 if let Some(data) = state.files.get(&path) {
497 return Ok(Metadata {
498 is_dir: false,
499 size: data.content.len() as u64,
500 created: data.created,
501 modified: data.modified,
502 accessed: data.accessed,
503 });
504 }
505
506 if state.directories.contains(&path) {
508 let now = SystemTime::now();
509 return Ok(Metadata {
510 is_dir: true,
511 size: 0,
512 created: now,
513 modified: now,
514 accessed: now,
515 });
516 }
517
518 Err(VfsError::NotFound(path))
519 }
520
521 async fn mkdir(&self, path: &str) -> VfsResult<()> {
522 let path = Self::normalize_path(path)?;
523 let mut state = self.state.write().await;
524
525 Self::check_parent_exists_with_state(&state, &path)?;
526
527 if state.files.contains_key(&path) {
529 return Err(VfsError::AlreadyExists(path));
530 }
531 if state.directories.contains(&path) {
532 return Err(VfsError::AlreadyExists(path));
533 }
534
535 state.directories.insert(path);
536 Ok(())
537 }
538
539 async fn rmdir(&self, path: &str) -> VfsResult<()> {
540 let path = Self::normalize_path(path)?;
541
542 if path == "/" {
543 return Err(VfsError::PermissionDenied(
544 "cannot remove root directory".to_string(),
545 ));
546 }
547
548 let mut state = self.state.write().await;
549
550 if !state.directories.contains(&path) {
552 if state.files.contains_key(&path) {
553 return Err(VfsError::NotDirectory(path));
554 } else {
555 return Err(VfsError::NotFound(path));
556 }
557 }
558
559 let prefix = format!("{path}/");
561 for file_path in state.files.keys() {
562 if file_path.starts_with(&prefix) {
563 return Err(VfsError::DirectoryNotEmpty(path));
564 }
565 }
566 for dir_path in &state.directories {
567 if dir_path.starts_with(&prefix) {
568 return Err(VfsError::DirectoryNotEmpty(path));
569 }
570 }
571
572 state.directories.remove(&path);
573 Ok(())
574 }
575
576 async fn rename(&self, from: &str, to: &str) -> VfsResult<()> {
577 let from = Self::normalize_path(from)?;
578 let to = Self::normalize_path(to)?;
579
580 if from == to {
581 return Ok(());
582 }
583
584 let mut state = self.state.write().await;
585
586 Self::check_parent_exists_with_state(&state, &to)?;
587
588 if state.files.contains_key(&from) {
590 if state.directories.contains(&to) {
592 return Err(VfsError::AlreadyExists(to));
593 }
594
595 if let Some(data) = state.files.remove(&from) {
596 state.files.insert(to, data);
597 return Ok(());
598 }
599 }
600
601 if state.directories.contains(&from) {
603 if state.files.contains_key(&to) {
605 return Err(VfsError::AlreadyExists(to));
606 }
607
608 let from_prefix = format!("{from}/");
610 let to_prefix = format!("{to}/");
611
612 let files_to_rename: Vec<_> = state
614 .files
615 .keys()
616 .filter(|p| p.starts_with(&from_prefix))
617 .cloned()
618 .collect();
619 for old_path in files_to_rename {
620 if let Some(data) = state.files.remove(&old_path) {
621 let new_path = old_path.replacen(&from_prefix, &to_prefix, 1);
622 state.files.insert(new_path, data);
623 }
624 }
625
626 let dirs_to_rename: Vec<_> = state
628 .directories
629 .iter()
630 .filter(|p| *p == &from || p.starts_with(&from_prefix))
631 .cloned()
632 .collect();
633 for old_path in dirs_to_rename {
634 state.directories.remove(&old_path);
635 let new_path = if old_path == from {
636 to.clone()
637 } else {
638 old_path.replacen(&from_prefix, &to_prefix, 1)
639 };
640 state.directories.insert(new_path);
641 }
642
643 return Ok(());
644 }
645
646 Err(VfsError::NotFound(from))
647 }
648
649 fn mkdir_sync(&self, path: &str) -> VfsResult<()> {
650 let path = Self::normalize_path(path)?;
651
652 let mut dirs_to_create = Vec::new();
654 let mut current = String::new();
655 for component in path.split('/').filter(|s| !s.is_empty()) {
656 current = format!("{}/{}", current, component);
657 dirs_to_create.push(current.clone());
658 }
659
660 if let Ok(mut state) = self.state.try_write() {
663 for dir in dirs_to_create {
664 state.directories.insert(dir);
665 }
666 return Ok(());
667 }
668
669 if let Ok(handle) = tokio::runtime::Handle::try_current() {
672 handle.block_on(async {
676 let mut state = self.state.write().await;
677 for dir in dirs_to_create {
678 state.directories.insert(dir);
679 }
680 });
681 } else {
682 let mut state = self.state.blocking_write();
684 for dir in dirs_to_create {
685 state.directories.insert(dir);
686 }
687 }
688
689 Ok(())
690 }
691}
692
693#[cfg(test)]
694#[allow(clippy::unwrap_used)]
695mod tests {
696 use super::*;
697
698 #[tokio::test]
699 async fn test_file_operations() {
700 let storage = InMemoryStorage::new();
701
702 storage.write("/test.txt", b"hello").await.unwrap();
704 let content = storage.read("/test.txt").await.unwrap();
705 assert_eq!(content, b"hello");
706
707 let partial = storage.read_at("/test.txt", 2, 3).await.unwrap();
709 assert_eq!(partial, b"llo");
710
711 storage.write("/test.txt", b"world").await.unwrap();
713 let content = storage.read("/test.txt").await.unwrap();
714 assert_eq!(content, b"world");
715
716 storage.delete("/test.txt").await.unwrap();
718 assert!(storage.read("/test.txt").await.is_err());
719 }
720
721 #[tokio::test]
722 async fn test_directory_operations() {
723 let storage = InMemoryStorage::new();
724
725 storage.mkdir("/subdir").await.unwrap();
727 assert!(storage.exists("/subdir").await.unwrap());
728
729 storage.write("/subdir/file.txt", b"content").await.unwrap();
731
732 let entries = storage.list("/subdir").await.unwrap();
734 assert_eq!(entries.len(), 1);
735 assert_eq!(entries[0].name, "file.txt");
736
737 assert!(storage.rmdir("/subdir").await.is_err());
739
740 storage.delete("/subdir/file.txt").await.unwrap();
742 storage.rmdir("/subdir").await.unwrap();
743 assert!(!storage.exists("/subdir").await.unwrap());
744 }
745
746 #[tokio::test]
747 async fn test_path_normalization() {
748 let storage = InMemoryStorage::new();
749
750 storage.write("/test.txt", b"data").await.unwrap();
751
752 assert!(storage.exists("/test.txt").await.unwrap());
754 assert!(storage.exists("/./test.txt").await.unwrap());
755
756 storage.mkdir("/dir").await.unwrap();
758 storage.write("/dir/file.txt", b"data").await.unwrap();
759 let content = storage.read("/dir/../dir/file.txt").await.unwrap();
760 assert_eq!(content, b"data");
761 }
762
763 #[tokio::test]
764 async fn test_rename() {
765 let storage = InMemoryStorage::new();
766
767 storage.write("/old.txt", b"content").await.unwrap();
769 storage.rename("/old.txt", "/new.txt").await.unwrap();
770 assert!(!storage.exists("/old.txt").await.unwrap());
771 assert!(storage.exists("/new.txt").await.unwrap());
772
773 storage.mkdir("/olddir").await.unwrap();
775 storage.write("/olddir/file.txt", b"data").await.unwrap();
776 storage.rename("/olddir", "/newdir").await.unwrap();
777 assert!(!storage.exists("/olddir").await.unwrap());
778 assert!(storage.exists("/newdir").await.unwrap());
779 assert!(storage.exists("/newdir/file.txt").await.unwrap());
780 }
781
782 #[tokio::test]
783 async fn test_stat() {
784 let storage = InMemoryStorage::new();
785
786 storage.write("/file.txt", b"hello").await.unwrap();
787 let meta = storage.stat("/file.txt").await.unwrap();
788 assert!(!meta.is_dir);
789 assert_eq!(meta.size, 5);
790
791 storage.mkdir("/dir").await.unwrap();
792 let meta = storage.stat("/dir").await.unwrap();
793 assert!(meta.is_dir);
794 }
795
796 #[tokio::test]
797 async fn test_write_at() {
798 let storage = InMemoryStorage::new();
799
800 storage.write_at("/file.txt", 5, b"world").await.unwrap();
802 let content = storage.read("/file.txt").await.unwrap();
803 assert_eq!(content.len(), 10);
804 assert_eq!(&content[5..], b"world");
805 assert_eq!(&content[0..5], &[0, 0, 0, 0, 0]);
806
807 storage.write_at("/file.txt", 0, b"hello").await.unwrap();
809 let content = storage.read("/file.txt").await.unwrap();
810 assert_eq!(&content, b"helloworld");
811 }
812
813 #[test]
814 fn test_mkdir_sync() {
815 let storage = InMemoryStorage::new();
816
817 storage.mkdir_sync("/data").unwrap();
819
820 let state = storage.state.blocking_read();
822 assert!(state.directories.contains("/data"));
823 }
824
825 #[test]
826 fn test_mkdir_sync_nested() {
827 let storage = InMemoryStorage::new();
828
829 storage.mkdir_sync("/data/subdir/nested").unwrap();
831
832 let state = storage.state.blocking_read();
834 assert!(state.directories.contains("/data"));
835 assert!(state.directories.contains("/data/subdir"));
836 assert!(state.directories.contains("/data/subdir/nested"));
837 }
838}