use std::{
fmt::Debug,
fs,
os::unix::fs::PermissionsExt,
path::{Path, PathBuf},
};
use crate::{
errors::*,
sys::{Entry, PathExt, Stdfs, VfsEntry},
trying,
};
#[derive(Debug, PartialEq, Eq)]
pub struct StdfsEntry {
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) follow: bool, pub(crate) cached: bool, }
impl Default for StdfsEntry {
fn default() -> Self {
Self {
path: PathBuf::new(),
alt: PathBuf::new(),
rel: PathBuf::new(),
dir: false,
file: false,
link: false,
mode: 0,
follow: false,
cached: false,
}
}
}
impl Clone for StdfsEntry {
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,
follow: self.follow,
cached: self.cached,
}
}
}
impl StdfsEntry {
pub(crate) fn from<T: AsRef<Path>>(path: T) -> RvResult<Self> {
let path = Stdfs::abs(path)?;
if !Stdfs::exists(&path) {
return Err(PathError::does_not_exist(&path).into());
}
let mut link = false;
let mut alt = PathBuf::new();
let mut rel = PathBuf::new();
let mut meta = fs::symlink_metadata(&path)?;
if meta.file_type().is_symlink() {
link = true;
let target = fs::read_link(&path)?;
alt = Stdfs::abs(if !target.is_absolute() { path.dir()?.mash(target) } else { target })?;
rel = alt.relative(path.dir()?)?;
meta = fs::metadata(&path)?;
}
Ok(StdfsEntry {
path,
alt,
rel,
dir: meta.is_dir(),
file: meta.is_file(),
link,
mode: meta.permissions().mode(),
follow: false,
cached: true,
})
}
}
impl Entry for StdfsEntry {
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::Stdfs(self)
}
}
#[derive(Debug)]
pub(crate) struct StdfsEntryIter {
pub(crate) dir: fs::ReadDir,
}
impl Iterator for StdfsEntryIter {
type Item = RvResult<VfsEntry>;
fn next(&mut self) -> Option<RvResult<VfsEntry>> {
if let Some(value) = self.dir.next() {
return Some(match StdfsEntry::from(trying!(value).path()) {
Ok(x) => Ok(x.upcast()),
Err(e) => Err(e),
});
}
None
}
}