Skip to main content

libimagstore/file_abstraction/
inmemory.rs

1//
2// imag - the personal information management suite for the commandline
3// Copyright (C) 2015-2020 Matthias Beyer <mail@beyermatthias.de> and contributors
4//
5// This library is free software; you can redistribute it and/or
6// modify it under the terms of the GNU Lesser General Public
7// License as published by the Free Software Foundation; version
8// 2.1 of the License.
9//
10// This library is distributed in the hope that it will be useful,
11// but WITHOUT ANY WARRANTY; without even the implied warranty of
12// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13// Lesser General Public License for more details.
14//
15// You should have received a copy of the GNU Lesser General Public
16// License along with this library; if not, write to the Free Software
17// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
18//
19
20use std::path::PathBuf;
21use std::collections::HashMap;
22use std::sync::Mutex;
23use std::cell::RefCell;
24use std::sync::Arc;
25use std::ops::Deref;
26
27use libimagerror::errors::ErrorMsg as EM;
28
29use failure::Fallible as Result;
30use failure::Error;
31use failure::err_msg;
32
33use super::FileAbstraction;
34use super::FileAbstractionInstance;
35use super::Drain;
36use crate::store::Entry;
37use crate::storeid::StoreIdWithBase;
38use crate::file_abstraction::iter::PathIterator;
39use crate::file_abstraction::iter::PathIterBuilder;
40
41type Backend = Arc<Mutex<RefCell<HashMap<PathBuf, Entry>>>>;
42
43/// `FileAbstraction` type, this is the Test version!
44///
45/// A lazy file is either absent, but a path to it is available, or it is present.
46#[derive(Debug)]
47pub struct InMemoryFileAbstractionInstance {
48    fs_abstraction: Backend,
49    absent_path: PathBuf,
50}
51
52impl InMemoryFileAbstractionInstance {
53
54    pub fn new(fs: Backend, pb: PathBuf) -> InMemoryFileAbstractionInstance {
55        InMemoryFileAbstractionInstance {
56            fs_abstraction: fs,
57            absent_path: pb
58        }
59    }
60
61}
62
63impl FileAbstractionInstance for InMemoryFileAbstractionInstance {
64
65    /**
66     * Get the mutable file behind a InMemoryFileAbstraction object
67     */
68    fn get_file_content(&mut self, _: StoreIdWithBase<'_>) -> Result<Option<Entry>> {
69        debug!("Getting lazy file: {:?}", self);
70
71        self.fs_abstraction
72            .lock()
73            .map_err(|_| Error::from(EM::LockError))
74            .map(|mut mtx| {
75                mtx.get_mut()
76                    .get(&self.absent_path)
77                    .cloned()
78            })
79            .map_err(Error::from)
80    }
81
82    fn write_file_content(&mut self, buf: &Entry) -> Result<()> {
83        let absent_path = &self.absent_path;
84        let mut mtx = self.fs_abstraction.lock().expect("Locking Mutex failed");
85        let backend = mtx.get_mut();
86        let _ = backend.insert(absent_path.clone(), buf.clone());
87        Ok(())
88    }
89}
90
91#[derive(Debug, Default)]
92pub struct InMemoryFileAbstraction {
93    virtual_filesystem: Backend,
94}
95
96impl InMemoryFileAbstraction {
97
98    pub fn backend(&self) -> &Backend {
99        &self.virtual_filesystem
100    }
101
102    fn backend_cloned(&self) -> Result<HashMap<PathBuf, Entry>> {
103        self.virtual_filesystem
104            .lock()
105            .map_err(|_| Error::from(EM::LockError))
106            .map(|mtx| mtx.deref().borrow().clone())
107    }
108
109}
110
111impl FileAbstraction for InMemoryFileAbstraction {
112
113    fn remove_file(&self, path: &PathBuf) -> Result<()> {
114        debug!("Removing: {:?}", path);
115        self.backend()
116            .lock()
117            .expect("Locking Mutex failed")
118            .get_mut()
119            .remove(path)
120            .map(|_| ())
121            .ok_or_else(|| EM::FileNotFound.into())
122    }
123
124    fn copy(&self, from: &PathBuf, to: &PathBuf) -> Result<()> {
125        debug!("Copying : {:?} -> {:?}", from, to);
126        let mut mtx = self.backend().lock().expect("Locking Mutex failed");
127        let backend = mtx.get_mut();
128
129        let a = backend.get(from).cloned().ok_or_else(|| EM::FileNotFound)?;
130        backend.insert(to.clone(), a);
131        debug!("Copying: {:?} -> {:?} worked", from, to);
132        Ok(())
133    }
134
135    fn rename(&self, from: &PathBuf, to: &PathBuf) -> Result<()> {
136        debug!("Renaming: {:?} -> {:?}", from, to);
137        let mut mtx = self.backend().lock().expect("Locking Mutex failed");
138        let backend = mtx.get_mut();
139
140        let a = backend.remove(from).ok_or_else(|| EM::FileNotFound)?;
141        let new_entry = {
142            let new_location = if to.starts_with("/") {
143                let s = to.to_str().map(String::from).ok_or_else(|| err_msg("Failed to convert path to str"))?;
144                PathBuf::from(s.replace("/", ""))
145            } else {
146                to.to_path_buf()
147            };
148
149            Entry::from_str(crate::storeid::StoreId::new(new_location)?, &a.to_str()?)?
150        };
151
152        backend.insert(to.clone(), new_entry);
153        debug!("Renaming: {:?} -> {:?} worked", from, to);
154        Ok(())
155    }
156
157    fn create_dir_all(&self, _: &PathBuf) -> Result<()> {
158        Ok(())
159    }
160
161    fn exists(&self, pb: &PathBuf) -> Result<bool> {
162        let mut mtx = self.backend().lock().expect("Locking Mutex failed");
163        let backend = mtx.get_mut();
164
165        Ok(backend.contains_key(pb))
166    }
167
168    fn is_file(&self, pb: &PathBuf) -> Result<bool> {
169        // Because we only store Entries in the memory-internal backend, we only have to check for
170        // existance here, as if a path exists in the inmemory storage, it is always mapped to an
171        // entry. hence it is always a path to a file
172        self.exists(pb)
173    }
174
175    fn new_instance(&self, p: PathBuf) -> Box<dyn FileAbstractionInstance> {
176        Box::new(InMemoryFileAbstractionInstance::new(self.backend().clone(), p))
177    }
178
179    fn drain(&self) -> Result<Drain> {
180        self.backend_cloned().map(Drain::new)
181    }
182
183    fn fill(&mut self, mut d: Drain) -> Result<()> {
184        debug!("Draining into : {:?}", self);
185        let mut mtx = self.backend()
186            .lock()
187            .map_err(|_| EM::LockError)?;
188        let backend = mtx.get_mut();
189
190        for (path, element) in d.iter() {
191            debug!("Drain into {:?}: {:?}", self, path);
192            backend.insert(path, element);
193        }
194
195        Ok(())
196    }
197
198    fn pathes_recursively<'a>(&self, _basepath: PathBuf, storepath: &'a PathBuf, backend: Arc<dyn FileAbstraction>) -> Result<PathIterator<'a>> {
199        trace!("Building PathIterator object (inmemory implementation)");
200        let keys : Vec<PathBuf> = self
201            .backend()
202            .lock()
203            .map_err(|_| EM::LockError)?
204            .get_mut()
205            .keys()
206            .map(PathBuf::from)
207            .map(Ok)
208            .collect::<Result<_>>()?; // we have to collect() because of the lock() above.
209
210        Ok(PathIterator::new(Box::new(InMemPathIterBuilder(keys)), storepath, backend))
211    }
212}
213
214#[derive(Debug)]
215pub struct InMemPathIterBuilder(Vec<PathBuf>);
216
217impl PathIterBuilder for InMemPathIterBuilder {
218    fn build_iter(&self) -> Box<dyn Iterator<Item = Result<PathBuf>>> {
219        Box::new(self.0.clone().into_iter().map(Ok))
220    }
221
222    fn in_collection(&mut self, c: &str) -> Result<()> {
223        debug!("Altering PathIterBuilder path with: {:?}", c);
224        self.0.retain(|p| p.starts_with(c));
225        debug!(" -> path : {:?}", self.0);
226        Ok(())
227    }
228}
229