use std::path::PathBuf;
use std::collections::HashMap;
use std::sync::Mutex;
use std::cell::RefCell;
use std::sync::Arc;
use std::ops::Deref;
use libimagerror::errors::ErrorMsg as EM;
use failure::Fallible as Result;
use failure::Error;
use failure::err_msg;
use super::FileAbstraction;
use super::FileAbstractionInstance;
use super::Drain;
use crate::store::Entry;
use crate::storeid::StoreIdWithBase;
use crate::file_abstraction::iter::PathIterator;
use crate::file_abstraction::iter::PathIterBuilder;
type Backend = Arc<Mutex<RefCell<HashMap<PathBuf, Entry>>>>;
#[derive(Debug)]
pub struct InMemoryFileAbstractionInstance {
fs_abstraction: Backend,
absent_path: PathBuf,
}
impl InMemoryFileAbstractionInstance {
pub fn new(fs: Backend, pb: PathBuf) -> InMemoryFileAbstractionInstance {
InMemoryFileAbstractionInstance {
fs_abstraction: fs,
absent_path: pb
}
}
}
impl FileAbstractionInstance for InMemoryFileAbstractionInstance {
fn get_file_content(&mut self, _: StoreIdWithBase<'_>) -> Result<Option<Entry>> {
debug!("Getting lazy file: {:?}", self);
self.fs_abstraction
.lock()
.map_err(|_| Error::from(EM::LockError))
.map(|mut mtx| {
mtx.get_mut()
.get(&self.absent_path)
.cloned()
})
.map_err(Error::from)
}
fn write_file_content(&mut self, buf: &Entry) -> Result<()> {
let absent_path = &self.absent_path;
let mut mtx = self.fs_abstraction.lock().expect("Locking Mutex failed");
let backend = mtx.get_mut();
let _ = backend.insert(absent_path.clone(), buf.clone());
Ok(())
}
}
#[derive(Debug, Default)]
pub struct InMemoryFileAbstraction {
virtual_filesystem: Backend,
}
impl InMemoryFileAbstraction {
pub fn backend(&self) -> &Backend {
&self.virtual_filesystem
}
fn backend_cloned(&self) -> Result<HashMap<PathBuf, Entry>> {
self.virtual_filesystem
.lock()
.map_err(|_| Error::from(EM::LockError))
.map(|mtx| mtx.deref().borrow().clone())
}
}
impl FileAbstraction for InMemoryFileAbstraction {
fn remove_file(&self, path: &PathBuf) -> Result<()> {
debug!("Removing: {:?}", path);
self.backend()
.lock()
.expect("Locking Mutex failed")
.get_mut()
.remove(path)
.map(|_| ())
.ok_or_else(|| EM::FileNotFound.into())
}
fn copy(&self, from: &PathBuf, to: &PathBuf) -> Result<()> {
debug!("Copying : {:?} -> {:?}", from, to);
let mut mtx = self.backend().lock().expect("Locking Mutex failed");
let backend = mtx.get_mut();
let a = backend.get(from).cloned().ok_or_else(|| EM::FileNotFound)?;
backend.insert(to.clone(), a);
debug!("Copying: {:?} -> {:?} worked", from, to);
Ok(())
}
fn rename(&self, from: &PathBuf, to: &PathBuf) -> Result<()> {
debug!("Renaming: {:?} -> {:?}", from, to);
let mut mtx = self.backend().lock().expect("Locking Mutex failed");
let backend = mtx.get_mut();
let a = backend.remove(from).ok_or_else(|| EM::FileNotFound)?;
let new_entry = {
let new_location = if to.starts_with("/") {
let s = to.to_str().map(String::from).ok_or_else(|| err_msg("Failed to convert path to str"))?;
PathBuf::from(s.replace("/", ""))
} else {
to.to_path_buf()
};
Entry::from_str(crate::storeid::StoreId::new(new_location)?, &a.to_str()?)?
};
backend.insert(to.clone(), new_entry);
debug!("Renaming: {:?} -> {:?} worked", from, to);
Ok(())
}
fn create_dir_all(&self, _: &PathBuf) -> Result<()> {
Ok(())
}
fn exists(&self, pb: &PathBuf) -> Result<bool> {
let mut mtx = self.backend().lock().expect("Locking Mutex failed");
let backend = mtx.get_mut();
Ok(backend.contains_key(pb))
}
fn is_file(&self, pb: &PathBuf) -> Result<bool> {
self.exists(pb)
}
fn new_instance(&self, p: PathBuf) -> Box<dyn FileAbstractionInstance> {
Box::new(InMemoryFileAbstractionInstance::new(self.backend().clone(), p))
}
fn drain(&self) -> Result<Drain> {
self.backend_cloned().map(Drain::new)
}
fn fill(&mut self, mut d: Drain) -> Result<()> {
debug!("Draining into : {:?}", self);
let mut mtx = self.backend()
.lock()
.map_err(|_| EM::LockError)?;
let backend = mtx.get_mut();
for (path, element) in d.iter() {
debug!("Drain into {:?}: {:?}", self, path);
backend.insert(path, element);
}
Ok(())
}
fn pathes_recursively<'a>(&self, _basepath: PathBuf, storepath: &'a PathBuf, backend: Arc<dyn FileAbstraction>) -> Result<PathIterator<'a>> {
trace!("Building PathIterator object (inmemory implementation)");
let keys : Vec<PathBuf> = self
.backend()
.lock()
.map_err(|_| EM::LockError)?
.get_mut()
.keys()
.map(PathBuf::from)
.map(Ok)
.collect::<Result<_>>()?;
Ok(PathIterator::new(Box::new(InMemPathIterBuilder(keys)), storepath, backend))
}
}
#[derive(Debug)]
pub struct InMemPathIterBuilder(Vec<PathBuf>);
impl PathIterBuilder for InMemPathIterBuilder {
fn build_iter(&self) -> Box<dyn Iterator<Item = Result<PathBuf>>> {
Box::new(self.0.clone().into_iter().map(Ok))
}
fn in_collection(&mut self, c: &str) -> Result<()> {
debug!("Altering PathIterBuilder path with: {:?}", c);
self.0.retain(|p| p.starts_with(c));
debug!(" -> path : {:?}", self.0);
Ok(())
}
}