Skip to main content

pijul_core/working_copy/
memory.rs

1use super::*;
2use crate::HashMap;
3use crate::pristine::InodeMetadata;
4use parking_lot::Mutex;
5use std::sync::Arc;
6use std::time::SystemTime;
7
8#[derive(Debug, Clone)]
9pub struct Memory(Arc<Mutex<Memory_>>);
10
11#[derive(Debug)]
12struct Memory_ {
13    files: FileTree,
14    last_modified: SystemTime,
15}
16
17#[derive(Debug, Default)]
18struct FileTree {
19    children: HashMap<String, Inode>,
20}
21#[derive(Debug)]
22enum Inode {
23    File {
24        meta: InodeMetadata,
25        last_modified: SystemTime,
26        contents: Arc<Mutex<Vec<u8>>>,
27    },
28    Directory {
29        meta: InodeMetadata,
30        last_modified: SystemTime,
31        children: FileTree,
32    },
33}
34
35impl Default for Memory {
36    fn default() -> Self {
37        Memory(Arc::new(Mutex::new(Memory_ {
38            files: FileTree::default(),
39            last_modified: SystemTime::now(),
40        })))
41    }
42}
43
44impl Memory {
45    pub fn new() -> Self {
46        Self::default()
47    }
48    pub fn list_files(&self) -> Vec<String> {
49        let m = self.0.lock();
50        let mut result = Vec::new();
51        let mut current_files = vec![(String::new(), &m.files)];
52        let mut next_files = Vec::new();
53        loop {
54            if current_files.is_empty() {
55                break;
56            }
57            for (path, tree) in current_files.iter() {
58                for (name, inode) in tree.children.iter() {
59                    let mut path = path.clone();
60                    crate::path::push(&mut path, name);
61                    match inode {
62                        Inode::File { .. } => {
63                            result.push(path);
64                        }
65                        Inode::Directory { children, .. } => {
66                            result.push(path.clone());
67                            next_files.push((path, children))
68                        }
69                    }
70                }
71            }
72            std::mem::swap(&mut current_files, &mut next_files);
73            next_files.clear();
74        }
75        result
76    }
77
78    pub fn add_file(&self, file: &str, file_contents: Vec<u8>) {
79        let file_meta = InodeMetadata::new(0, false);
80        let last = SystemTime::now();
81        self.add_inode(
82            file,
83            Inode::File {
84                meta: file_meta,
85                last_modified: last,
86                contents: Arc::new(Mutex::new(file_contents)),
87            },
88        )
89    }
90
91    pub fn add_dir(&self, file: &str) {
92        let file_meta = InodeMetadata::new(0o100, true);
93        let last = SystemTime::now();
94        self.add_inode(
95            file,
96            Inode::Directory {
97                meta: file_meta,
98                last_modified: last,
99                children: FileTree {
100                    children: HashMap::default(),
101                },
102            },
103        )
104    }
105
106    fn add_inode(&self, file: &str, inode: Inode) {
107        let mut m = self.0.lock();
108        let last = SystemTime::now();
109        m.last_modified = last;
110        debug!("add_inode {:?} {:?}", last, inode);
111        let mut file_tree = &mut m.files;
112        let file = file.split('/').filter(|c| !c.is_empty());
113        let mut p = file.peekable();
114        while let Some(f) = p.next() {
115            if p.peek().is_some() {
116                let entry = file_tree
117                    .children
118                    .entry(f.to_string())
119                    .or_insert(Inode::Directory {
120                        meta: InodeMetadata::new(0o100, true),
121                        children: FileTree {
122                            children: HashMap::default(),
123                        },
124                        last_modified: last,
125                    });
126                match *entry {
127                    Inode::Directory {
128                        ref mut children, ..
129                    } => file_tree = children,
130                    _ => panic!("Not a directory"),
131                }
132            } else {
133                file_tree.children.insert(f.to_string(), inode);
134                break;
135            }
136        }
137    }
138}
139
140impl Memory_ {
141    fn get_file(&self, file: &str) -> Option<&Inode> {
142        debug!("get_file {:?}", file);
143        debug!("repo = {:?}", self);
144        let mut t = Some(&self.files);
145        let mut inode = None;
146        let it = file.split('/').filter(|c| !c.is_empty());
147        for c in it {
148            debug!("c = {:?}", c);
149            inode = t.take().unwrap().children.get(c);
150            debug!("inode = {:?}", inode);
151            match inode {
152                Some(Inode::Directory { children, .. }) => t = Some(children),
153                _ => break,
154            }
155        }
156        inode
157    }
158
159    fn get_file_mut<'a>(&'a mut self, file: &str) -> Option<&'a mut Inode> {
160        debug!("get_file_mut {:?}", file);
161        debug!("repo = {:?}", self);
162        let mut t = Some(&mut self.files);
163        let mut it = file.split('/').filter(|c| !c.is_empty()).peekable();
164        self.last_modified = SystemTime::now();
165        while let Some(c) = it.next() {
166            debug!("c = {:?}", c);
167            let inode_ = t.take().unwrap().children.get_mut(c);
168            debug!("inode = {:?}", inode_);
169            if it.peek().is_none() {
170                return inode_;
171            }
172            match inode_ {
173                Some(Inode::Directory { children, .. }) => t = Some(children),
174                _ => return None,
175            }
176        }
177        None
178    }
179
180    fn remove_path_(&mut self, path: &str) -> Option<Inode> {
181        debug!("remove_path {:?}", path);
182        debug!("repo = {:?}", self);
183        self.last_modified = SystemTime::now();
184        let mut t = Some(&mut self.files);
185        let mut it = path.split('/').filter(|c| !c.is_empty());
186        let mut c = it.next().unwrap();
187        loop {
188            debug!("c = {:?}", c);
189            let next_c = it.next();
190            let t_ = t.take().unwrap();
191            let next_c = if let Some(next_c) = next_c {
192                next_c
193            } else {
194                return t_.children.remove(c);
195            };
196            let inode = t_.children.get_mut(c);
197            c = next_c;
198            debug!("inode = {:?}", inode);
199            match inode {
200                Some(Inode::Directory { children, .. }) => t = Some(children),
201                _ => return None,
202            }
203        }
204    }
205}
206
207#[derive(Debug, Error)]
208pub enum Error {
209    #[error("Path not found: {path}")]
210    NotFound { path: String },
211}
212
213impl WorkingCopyRead for Memory {
214    type Error = Error;
215    fn file_metadata(&self, file: &str) -> Result<InodeMetadata, Self::Error> {
216        let m = self.0.lock();
217        match m.get_file(file) {
218            Some(Inode::Directory { meta, .. }) => Ok(*meta),
219            Some(Inode::File { meta, .. }) => Ok(*meta),
220            None => Err(Error::NotFound {
221                path: file.to_string(),
222            }),
223        }
224    }
225    fn read_file(&self, file: &str, buffer: &mut Vec<u8>) -> Result<(), Self::Error> {
226        let m = self.0.lock();
227        match m.get_file(file) {
228            Some(Inode::Directory { .. }) => panic!("Not a file: {:?}", file),
229            Some(Inode::File { contents, .. }) => {
230                buffer.extend(&contents.lock()[..]);
231                Ok(())
232            }
233            None => Err(Error::NotFound {
234                path: file.to_string(),
235            }),
236        }
237    }
238    fn modified_time(&self, file: &str) -> Result<std::time::SystemTime, Self::Error> {
239        let m = self.0.lock();
240        match m.get_file(file) {
241            Some(Inode::Directory { last_modified, .. })
242            | Some(Inode::File { last_modified, .. }) => Ok(*last_modified),
243            _ => {
244                debug!("modified_time not found {:?}", file);
245                Ok(m.last_modified)
246            }
247        }
248    }
249}
250
251impl WorkingCopy for Memory {
252    fn create_dir_all(&self, file: &str) -> Result<(), Self::Error> {
253        let not_already_exists = {
254            let m = self.0.lock();
255            m.get_file(file).is_none()
256        };
257        if not_already_exists {
258            let last = SystemTime::now();
259            self.add_inode(
260                file,
261                Inode::Directory {
262                    meta: InodeMetadata::new(0o100, true),
263                    children: FileTree {
264                        children: HashMap::default(),
265                    },
266                    last_modified: last,
267                },
268            );
269        }
270        Ok(())
271    }
272
273    fn remove_path(&self, path: &str, _rec: bool) -> Result<(), Self::Error> {
274        self.0.lock().remove_path_(path);
275        Ok(())
276    }
277
278    fn rename(&self, old: &str, new: &str) -> Result<(), Self::Error> {
279        debug!("rename {:?} to {:?}", old, new);
280        let inode = {
281            let mut m = self.0.lock();
282            m.remove_path_(old)
283        };
284        if let Some(inode) = inode {
285            self.add_inode(new, inode)
286        }
287        Ok(())
288    }
289    fn set_permissions(&self, file: &str, permissions: u16) -> Result<(), Self::Error> {
290        debug!("set_permissions {:?}", file);
291        let mut m = self.0.lock();
292        match m.get_file_mut(file) {
293            Some(Inode::File {
294                meta,
295                last_modified,
296                ..
297            }) => {
298                *last_modified = SystemTime::now();
299                *meta = InodeMetadata::new(permissions as usize & 0o100, false);
300            }
301            Some(Inode::Directory {
302                meta,
303                last_modified,
304                ..
305            }) => {
306                *last_modified = SystemTime::now();
307                *meta = InodeMetadata::new(permissions as usize & 0o100, true);
308            }
309            None => panic!("file not found: {:?}", file),
310        }
311        Ok(())
312    }
313
314    type Writer = Writer;
315    fn write_file(&self, file: &str, _: crate::Inode) -> Result<Self::Writer, Self::Error> {
316        let mut m = self.0.lock();
317        if let Some(f) = m.get_file_mut(file) {
318            if let Inode::File {
319                contents,
320                last_modified,
321                ..
322            } = f
323            {
324                *last_modified = SystemTime::now();
325                contents.lock().clear();
326                return Ok(Writer {
327                    w: contents.clone(),
328                });
329            } else {
330                unreachable!()
331            }
332        }
333        std::mem::drop(m);
334        let contents = Arc::new(Mutex::new(Vec::new()));
335        let last_modified = SystemTime::now();
336        debug!("add_inode {:?}", last_modified);
337        self.add_inode(
338            file,
339            Inode::File {
340                meta: InodeMetadata::new(0, false),
341                contents: contents.clone(),
342                last_modified,
343            },
344        );
345        Ok(Writer { w: contents })
346    }
347
348    fn touch(&self, file: &str, time: std::time::SystemTime) -> Result<(), Self::Error> {
349        let mut m = self.0.lock();
350        if let Some(f) = m.get_file_mut(file) {
351            if let Inode::File { last_modified, .. } = f {
352                *last_modified = time
353            } else {
354                unreachable!()
355            }
356        }
357        Ok(())
358    }
359}
360
361pub struct Writer {
362    w: Arc<Mutex<Vec<u8>>>,
363}
364
365impl std::io::Write for Writer {
366    fn write(&mut self, b: &[u8]) -> Result<usize, std::io::Error> {
367        self.w.lock().write(b)
368    }
369    fn flush(&mut self) -> Result<(), std::io::Error> {
370        Ok(())
371    }
372}