exocore_core/dir/
ram.rs

1use std::{
2    collections::BTreeMap,
3    io::{Read, Seek, SeekFrom, Write},
4    sync::{Arc, RwLock},
5};
6
7use super::*;
8
9#[derive(Default)]
10pub struct RamDirectory {
11    files: Arc<RwLock<BTreeMap<PathBuf, RamFileData>>>,
12}
13
14impl RamDirectory {
15    pub fn new() -> Self {
16        Self::default()
17    }
18}
19
20impl Directory for RamDirectory {
21    fn open_read(&self, path: &Path) -> Result<Box<dyn FileRead>, Error> {
22        if path.parent().is_none() {
23            return Err(Error::Path(anyhow!("expected a non-root path to a file")));
24        }
25
26        let files = self.files.read().unwrap();
27        let file = files
28            .get(path)
29            .ok_or_else(|| Error::NotFound(path.to_path_buf()))?;
30
31        Ok(Box::new(RamFile {
32            data: file.clone(),
33            cursor: 0,
34        }))
35    }
36
37    fn open_write(&self, path: &Path) -> Result<Box<dyn FileWrite>, Error> {
38        if path.parent().is_none() {
39            return Err(Error::Path(anyhow!("expected a non-root path to a file")));
40        }
41
42        let mut files = self.files.write().unwrap();
43
44        let data = files.get(path).cloned();
45        let data = if let Some(data) = data {
46            data
47        } else {
48            let data = RamFileData::default();
49            files.insert(path.to_path_buf(), data.clone());
50            data
51        };
52
53        Ok(Box::new(RamFile { data, cursor: 0 }))
54    }
55
56    fn open_create(&self, path: &Path) -> Result<Box<dyn FileWrite>, Error> {
57        if path.parent().is_none() {
58            return Err(Error::Path(anyhow!("expected a non-root path to a file")));
59        }
60
61        let mut files = self.files.write().unwrap();
62
63        let data = RamFileData::default();
64        files.insert(path.to_path_buf(), data.clone());
65
66        Ok(Box::new(RamFile { data, cursor: 0 }))
67    }
68
69    fn list(&self, prefix: Option<&Path>) -> Result<Vec<Box<dyn FileStat>>, Error> {
70        let prefix = prefix.unwrap_or_else(|| Path::new(""));
71        let files = self.files.read().unwrap();
72
73        let mut result: Vec<Box<dyn FileStat>> = Vec::new();
74        for (path, file) in files.iter() {
75            if path.starts_with(prefix) {
76                result.push(Box::new(RamFileStat {
77                    path: path.to_path_buf(),
78                    data: file.clone(),
79                }));
80            }
81        }
82
83        Ok(result)
84    }
85
86    fn stat(&self, path: &Path) -> Result<Box<dyn FileStat>, Error> {
87        let files = self.files.read().unwrap();
88        let file = files
89            .get(path)
90            .ok_or_else(|| Error::NotFound(path.to_path_buf()))?;
91
92        Ok(Box::new(RamFileStat {
93            path: path.to_path_buf(),
94            data: file.clone(),
95        }))
96    }
97
98    fn exists(&self, path: &Path) -> bool {
99        let files = self.files.read().unwrap();
100        files.contains_key(path)
101    }
102
103    fn delete(&self, path: &Path) -> Result<(), Error> {
104        let mut files = self.files.write().unwrap();
105        files.remove(path);
106        Ok(())
107    }
108
109    fn clone(&self) -> DynDirectory {
110        RamDirectory {
111            files: self.files.clone(),
112        }
113        .into()
114    }
115
116    fn as_os_path(&self) -> Result<PathBuf, Error> {
117        Err(Error::NotOsDirectory)
118    }
119}
120
121#[derive(Clone, Default)]
122pub struct RamFileData {
123    pub bytes: Arc<RwLock<Vec<u8>>>,
124}
125
126impl From<Vec<u8>> for RamFileData {
127    fn from(bytes: Vec<u8>) -> Self {
128        RamFileData {
129            bytes: Arc::new(RwLock::new(bytes)),
130        }
131    }
132}
133
134#[derive(Default)]
135pub struct RamFile {
136    data: RamFileData,
137    cursor: usize,
138}
139
140impl RamFile {
141    pub fn new(data: RamFileData) -> Self {
142        RamFile { data, cursor: 0 }
143    }
144}
145
146impl FileRead for RamFile {}
147
148impl FileWrite for RamFile {}
149
150impl Read for RamFile {
151    fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
152        let data = self.data.bytes.read().unwrap();
153        if self.cursor >= data.len() {
154            return Ok(0);
155        }
156
157        let to = std::cmp::min(self.cursor + buf.len(), data.len());
158        let count = (&data[self.cursor..to]).read(buf)?;
159        self.cursor += count;
160        Ok(count)
161    }
162}
163
164impl Seek for RamFile {
165    fn seek(&mut self, pos: SeekFrom) -> std::io::Result<u64> {
166        let data = self.data.bytes.read().unwrap();
167        let len = data.len();
168        let pos = match pos {
169            SeekFrom::Start(pos) => pos,
170            SeekFrom::End(pos) => (len as i64 + pos) as u64,
171            SeekFrom::Current(pos) => (self.cursor as i64 + pos) as u64,
172        };
173        self.cursor = pos as usize;
174        Ok(pos)
175    }
176}
177
178impl Write for RamFile {
179    fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
180        let mut data = self.data.bytes.write().unwrap();
181
182        for i in 0..buf.len() {
183            if self.cursor + i >= data.len() {
184                data.push(buf[i]);
185            } else {
186                data[self.cursor + i] = buf[i];
187            }
188        }
189
190        self.cursor += buf.len();
191
192        Ok(buf.len())
193    }
194
195    fn flush(&mut self) -> std::io::Result<()> {
196        Ok(())
197    }
198}
199
200pub struct RamFileStat {
201    path: PathBuf,
202    data: RamFileData,
203}
204
205impl FileStat for RamFileStat {
206    fn path(&self) -> &Path {
207        self.path.as_path()
208    }
209
210    fn size(&self) -> u64 {
211        self.data.bytes.read().unwrap().len() as u64
212    }
213}
214
215#[cfg(test)]
216mod tests {
217    use super::*;
218
219    #[test]
220    fn test_write_read_file() {
221        super::super::tests::test_write_read_file(RamDirectory::new());
222    }
223
224    #[test]
225    fn test_list() {
226        super::super::tests::test_list(RamDirectory::new());
227    }
228
229    #[test]
230    fn test_delete() {
231        super::super::tests::test_delete(RamDirectory::new());
232    }
233}