use std::{
collections::HashSet,
path::{Path, PathBuf},
sync::Arc,
};
use super::MemfsEntries;
use crate::{
errors::*,
sys::{Entry, PathExt, VfsEntry},
};
#[derive(Debug)]
pub(crate) struct MemfsEntryOpts {
path: PathBuf, alt: PathBuf, rel: PathBuf, dir: bool, file: bool, link: bool, mode: u32, gid: u32, uid: u32, }
impl MemfsEntryOpts {
pub(crate) fn build(self) -> MemfsEntry {
let opts = if !self.dir && !self.file && !self.link { self.dir() } else { self };
MemfsEntry {
files: if opts.dir { Some(HashSet::new()) } else { None },
path: opts.path,
alt: opts.alt,
rel: opts.rel,
dir: opts.dir,
file: opts.file,
link: opts.link,
mode: opts.mode,
gid: opts.gid,
uid: opts.uid,
follow: false,
cached: false,
}
}
pub(crate) fn dir(mut self) -> Self {
self.dir = true;
self.file = false;
let mode = if self.mode == 0 { None } else { Some(self.mode) };
self.mode(mode)
}
pub(crate) fn file(mut self) -> Self {
self.file = true;
self.dir = false;
let mode = if self.mode == 0 { None } else { Some(self.mode) };
self.mode(mode)
}
pub(crate) fn link_to<T: Into<PathBuf>>(mut self, path: T) -> RvResult<Self> {
self.link = true;
self.alt = path.into();
self.rel = self.alt.relative(self.path.dir()?)?;
Ok(self.mode(None))
}
pub(crate) fn _mode(mut self, mode: u32) -> Self {
self.mode = mode;
self
}
pub(crate) fn mode(mut self, mode: Option<u32>) -> Self {
let mode = mode.unwrap_or(if self.link {
0o120777
} else if self.file {
0o100644
} else {
0o40755
});
self.mode = if self.link {
mode | 0o120000
} else if self.file {
mode | 0o100000
} else if self.dir {
mode | 0o40000
} else {
mode
};
self
}
}
#[derive(Debug)]
pub struct MemfsEntry {
pub(crate) path: PathBuf, pub(crate) alt: PathBuf, pub(crate) rel: PathBuf, pub(crate) dir: bool, pub(crate) file: bool, pub(crate) link: bool, pub(crate) mode: u32, pub(crate) uid: u32, pub(crate) gid: u32, pub(crate) follow: bool, pub(crate) cached: bool, pub(crate) files: Option<HashSet<String>>, }
impl MemfsEntry {
pub(crate) fn opts<T: Into<PathBuf>>(path: T) -> MemfsEntryOpts {
MemfsEntryOpts {
path: path.into(),
alt: PathBuf::new(),
rel: PathBuf::new(),
dir: false,
file: false,
link: false,
mode: 0,
gid: 1000,
uid: 1000,
}
}
pub(crate) fn add<T: Into<String>>(&mut self, entry: T) -> RvResult<bool> {
let name = entry.into();
if !self.dir {
return Err(PathError::is_not_dir(&self.path).into());
}
if let Some(ref mut files) = self.files {
return Ok(files.insert(name.clone()));
} else {
let mut files = HashSet::new();
files.insert(name);
self.files = Some(files);
}
Ok(true)
}
#[allow(dead_code)]
pub(crate) fn downcast(vfs: VfsEntry) -> RvResult<MemfsEntry> {
match vfs {
VfsEntry::Memfs(x) => Ok(x),
_ => Err(VfsError::WrongProvider.into()),
}
}
pub(crate) fn remove<T: Into<String>>(&mut self, entry: T) -> RvResult<()> {
let name = entry.into();
if !self.dir {
return Err(PathError::is_not_dir(&self.path).into());
}
if let Some(ref mut files) = self.files {
files.remove(&name);
}
Ok(())
}
pub(crate) fn set_mode(&mut self, mode: Option<u32>) {
let opts = MemfsEntryOpts {
path: PathBuf::new(),
alt: PathBuf::new(),
rel: PathBuf::new(),
dir: self.dir,
file: self.file,
link: self.link,
mode: self.mode,
gid: self.gid,
uid: self.uid,
}
.mode(mode);
self.mode = opts.mode;
}
pub(crate) fn set_owner(&mut self, uid: Option<u32>, gid: Option<u32>) {
if let Some(uid) = uid {
self.uid = uid;
}
if let Some(gid) = gid {
self.gid = gid;
}
}
}
impl Entry for MemfsEntry {
fn path(&self) -> &Path {
&self.path
}
fn path_buf(&self) -> PathBuf {
self.path.clone()
}
fn alt(&self) -> &Path {
&self.alt
}
fn alt_buf(&self) -> PathBuf {
self.alt.clone()
}
fn rel(&self) -> &Path {
&self.rel
}
fn rel_buf(&self) -> PathBuf {
self.rel.clone()
}
fn follow(mut self, follow: bool) -> VfsEntry {
if follow && self.link && !self.follow {
self.follow = true;
std::mem::swap(&mut self.path, &mut self.alt);
}
self.upcast()
}
fn following(&self) -> bool {
self.follow
}
fn is_dir(&self) -> bool {
self.dir
}
fn is_file(&self) -> bool {
self.file
}
fn is_symlink(&self) -> bool {
self.link
}
fn mode(&self) -> u32 {
self.mode
}
fn upcast(self) -> VfsEntry {
VfsEntry::Memfs(self)
}
}
impl Clone for MemfsEntry {
fn clone(&self) -> Self {
Self {
path: self.path.clone(),
alt: self.alt.clone(),
rel: self.rel.clone(),
dir: self.dir,
file: self.file,
link: self.link,
mode: self.mode,
gid: self.gid,
uid: self.uid,
follow: self.follow,
cached: self.cached,
files: self.files.clone(),
}
}
}
pub(crate) struct MemfsEntryIter {
iter: Box<dyn Iterator<Item = PathBuf>>,
entries: Arc<MemfsEntries>,
}
impl MemfsEntryIter {
pub(crate) fn new<T: AsRef<Path>>(path: T, entries: Arc<MemfsEntries>) -> RvResult<Self> {
let path = path.as_ref();
if let Some(entry) = entries.get(path) {
let mut items = vec![];
if let Some(ref files) = entry.files {
for name in files.iter() {
items.push(path.mash(name));
}
}
Ok(MemfsEntryIter {
iter: Box::new(items.into_iter()),
entries,
})
} else {
Err(PathError::does_not_exist(path).into())
}
}
}
impl Iterator for MemfsEntryIter {
type Item = RvResult<VfsEntry>;
fn next(&mut self) -> Option<RvResult<VfsEntry>> {
if let Some(value) = self.iter.next() {
if let Some(x) = self.entries.get(&value) {
return Some(Ok(x.clone().upcast()));
}
}
None
}
}
#[cfg(test)]
mod tests {
use crate::prelude::*;
#[test]
fn test_uid() {
let mut entry = MemfsEntry::opts("").build();
assert_eq!(entry.gid, 1000);
assert_eq!(entry.uid, 1000);
entry.gid = 5;
entry.uid = 7;
assert_eq!(entry.gid, 5);
assert_eq!(entry.uid, 7);
}
#[test]
fn test_follow() {
let memfs = Memfs::new();
let path = memfs.root().mash("link");
let target = memfs.root().mash("target");
let entry = MemfsEntry::opts(&path).link_to(&target).unwrap().build();
assert_eq!(entry.path(), &path);
assert_eq!(entry.alt(), &target);
assert_eq!(entry.rel(), Path::new("target"));
let entry = entry.follow(true);
assert_eq!(entry.path(), &target);
assert_eq!(entry.alt(), &path);
assert_eq!(entry.rel(), Path::new("target"));
}
#[test]
fn test_file() {
let vfs = Memfs::new();
let path = vfs.root().mash("file");
let entry = MemfsEntry::opts(&path).file().build();
assert_eq!(&entry.path, &path);
assert_eq!(&entry.alt, &PathBuf::new());
assert_eq!(&entry.rel, &PathBuf::new());
assert_eq!(entry.dir, false);
assert_eq!(entry.file, true);
assert_eq!(entry.link, false);
assert_eq!(entry.follow, false);
assert_eq!(entry.cached, false);
assert_eq!(entry.mode, 0o100644);
assert_eq!(entry.files, None);
}
#[test]
fn test_dir() {
let vfs = Memfs::new();
let path = vfs.root().mash("dir");
let entry = MemfsEntry::opts(&path).dir().build();
assert_eq!(&entry.path, &path);
assert_eq!(&entry.alt, &PathBuf::new());
assert_eq!(&entry.rel, &PathBuf::new());
assert_eq!(entry.dir, true);
assert_eq!(entry.file, false);
assert_eq!(entry.link, false);
assert_eq!(entry.follow, false);
assert_eq!(entry.cached, false);
assert_eq!(entry.mode, 0o40755);
assert!(entry.files.is_some());
assert!(entry.files.unwrap().is_empty());
}
}