Skip to main content

oak_vfs/vfs/
memory.rs

1use crate::{FileMetadata, FileType, Vfs, WritableVfs};
2use oak_core::{
3    Arc,
4    source::{SourceId, SourceText},
5};
6use std::{
7    collections::HashMap,
8    sync::{
9        RwLock,
10        atomic::{AtomicU32, Ordering},
11    },
12};
13
14/// A memory-based Virtual File System implementation.
15/// Ideal for WASM environments or testing where physical disk access is not available.
16#[derive(Default)]
17pub struct MemoryVfs {
18    files: std::sync::Arc<RwLock<HashMap<Arc<str>, FileEntry>>>,
19    ids: std::sync::Arc<RwLock<HashMap<SourceId, Arc<str>>>>,
20    uri_to_id: std::sync::Arc<RwLock<HashMap<Arc<str>, SourceId>>>,
21    next_id: std::sync::Arc<AtomicU32>,
22}
23
24impl Clone for MemoryVfs {
25    fn clone(&self) -> Self {
26        Self { files: self.files.clone(), ids: self.ids.clone(), uri_to_id: self.uri_to_id.clone(), next_id: self.next_id.clone() }
27    }
28}
29
30struct FileEntry {
31    content: Arc<str>,
32    modified: u64,
33    id: SourceId,
34}
35
36impl MemoryVfs {
37    /// Create a new empty MemoryVfs.
38    pub fn new() -> Self {
39        Self::default()
40    }
41
42    /// Upsert a file's content in the memory VFS.
43    pub fn write_file(&self, uri: &str, content: impl Into<Arc<str>>) {
44        let uri_arc: Arc<str> = Arc::from(uri);
45        let mut files = self.files.write().unwrap();
46        let mut ids = self.ids.write().unwrap();
47        let mut uri_to_id = self.uri_to_id.write().unwrap();
48
49        let id = if let Some(id) = uri_to_id.get(&uri_arc) {
50            *id
51        }
52        else {
53            let id = self.next_id.fetch_add(1, Ordering::SeqCst);
54            uri_to_id.insert(uri_arc.clone(), id);
55            ids.insert(id, uri_arc.clone());
56            id
57        };
58
59        files.insert(uri_arc, FileEntry { content: content.into(), modified: Self::now(), id });
60    }
61
62    /// Remove a file from the memory VFS.
63    pub fn remove_file(&self, uri: &str) {
64        let mut files = self.files.write().unwrap();
65        let mut ids = self.ids.write().unwrap();
66        let mut uri_to_id = self.uri_to_id.write().unwrap();
67
68        if let Some(entry) = files.remove(uri) {
69            uri_to_id.remove(uri);
70            ids.remove(&entry.id);
71        }
72    }
73
74    fn now() -> u64 {
75        // In WASM, we'd typically use js-sys::Date::now()
76        // Here we use a simple timestamp or 0 if not available
77        #[cfg(not(target_arch = "wasm32"))]
78        {
79            std::time::SystemTime::now().duration_since(std::time::UNIX_EPOCH).unwrap_or_default().as_secs()
80        }
81        #[cfg(target_arch = "wasm32")]
82        {
83            0 // Simplified for now
84        }
85    }
86}
87
88impl Vfs for MemoryVfs {
89    type Source = SourceText;
90
91    fn get_source(&self, uri: &str) -> Option<SourceText> {
92        let files = self.files.read().unwrap();
93        files.get(uri).map(|entry| SourceText::new_with_id(entry.content.clone(), entry.id))
94    }
95
96    fn get_uri(&self, id: SourceId) -> Option<Arc<str>> {
97        let ids = self.ids.read().unwrap();
98        ids.get(&id).cloned()
99    }
100
101    fn get_id(&self, uri: &str) -> Option<SourceId> {
102        let uri_to_id = self.uri_to_id.read().unwrap();
103        uri_to_id.get(uri).cloned()
104    }
105
106    fn exists(&self, uri: &str) -> bool {
107        self.files.read().unwrap().contains_key(uri)
108    }
109
110    fn metadata(&self, uri: &str) -> Option<FileMetadata> {
111        let files = self.files.read().unwrap();
112        files.get(uri).map(|entry| FileMetadata { file_type: FileType::File, len: entry.content.len() as u64, modified: Some(entry.modified) })
113    }
114
115    fn read_dir(&self, _uri: &str) -> Option<Vec<Arc<str>>> {
116        // Basic implementation: return all keys for now
117        // A more complex one would handle directory hierarchy
118        let files = self.files.read().unwrap();
119        Some(files.keys().cloned().collect())
120    }
121}
122
123impl WritableVfs for MemoryVfs {
124    fn write_file(&self, uri: &str, content: Arc<str>) {
125        self.write_file(uri, content);
126    }
127
128    fn remove_file(&self, uri: &str) {
129        self.remove_file(uri);
130    }
131}