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}