use crate::path::virtual_path::VirtualPath;
use crate::validator::path_history::PathHistory;
use crate::PathBoundary;
use crate::Result;
use std::marker::PhantomData;
use std::path::Path;
#[derive(Clone)]
pub struct VirtualRoot<Marker = ()> {
pub(crate) root: PathBoundary<Marker>,
pub(crate) _marker: PhantomData<Marker>,
}
impl<Marker> VirtualRoot<Marker> {
#[inline]
pub fn try_new<P: AsRef<Path>>(root_path: P) -> Result<Self> {
let root = PathBoundary::try_new(root_path)?;
Ok(Self {
root,
_marker: PhantomData,
})
}
#[inline]
pub fn metadata(&self) -> std::io::Result<std::fs::Metadata> {
self.root.metadata()
}
#[inline]
pub fn into_virtualpath(self) -> Result<VirtualPath<Marker>> {
let strict_root = self.root.into_strictpath()?;
Ok(strict_root.virtualize())
}
#[inline]
pub fn change_marker<NewMarker>(self) -> VirtualRoot<NewMarker> {
let VirtualRoot { root, .. } = self;
VirtualRoot {
root: root.change_marker(),
_marker: PhantomData,
}
}
pub fn virtual_symlink<P: AsRef<Path>>(&self, link_path: P) -> std::io::Result<()> {
let link_ref = link_path.as_ref();
let validated_link = self
.virtual_join(link_ref)
.map_err(|err| std::io::Error::new(std::io::ErrorKind::Other, err))?;
let root = self
.root
.clone()
.into_strictpath()
.map_err(|err| std::io::Error::new(std::io::ErrorKind::Other, err))?;
root.strict_symlink(validated_link.as_unvirtual().path())
}
pub fn virtual_hard_link<P: AsRef<Path>>(&self, link_path: P) -> std::io::Result<()> {
let link_ref = link_path.as_ref();
let validated_link = self
.virtual_join(link_ref)
.map_err(|err| std::io::Error::new(std::io::ErrorKind::Other, err))?;
let root = self
.root
.clone()
.into_strictpath()
.map_err(|err| std::io::Error::new(std::io::ErrorKind::Other, err))?;
root.strict_hard_link(validated_link.as_unvirtual().path())
}
#[cfg(all(windows, feature = "junctions"))]
pub fn virtual_junction<P: AsRef<Path>>(&self, link_path: P) -> std::io::Result<()> {
let link_ref = link_path.as_ref();
let validated_link = self
.virtual_join(link_ref)
.map_err(|err| std::io::Error::new(std::io::ErrorKind::Other, err))?;
let root = self
.root
.clone()
.into_strictpath()
.map_err(|err| std::io::Error::new(std::io::ErrorKind::Other, err))?;
root.strict_junction(validated_link.as_unvirtual().path())
}
#[inline]
pub fn read_dir(&self) -> std::io::Result<std::fs::ReadDir> {
self.root.read_dir()
}
#[inline]
pub fn virtual_read_dir(&self) -> std::io::Result<VirtualRootReadDir<'_, Marker>> {
Ok(VirtualRootReadDir {
inner: self.root.read_dir()?,
vroot: self,
})
}
#[inline]
pub fn remove_dir(&self) -> std::io::Result<()> {
self.root.remove_dir()
}
#[inline]
pub fn remove_dir_all(&self) -> std::io::Result<()> {
self.root.remove_dir_all()
}
#[inline]
pub fn try_new_create<P: AsRef<Path>>(root_path: P) -> Result<Self> {
let root = PathBoundary::try_new_create(root_path)?;
Ok(Self {
root,
_marker: PhantomData,
})
}
#[inline]
pub fn virtual_join<P: AsRef<Path>>(&self, candidate_path: P) -> Result<VirtualPath<Marker>> {
let user_candidate = candidate_path.as_ref().to_path_buf();
let anchored = PathHistory::new(user_candidate).canonicalize_anchored(&self.root)?;
let validated = anchored.boundary_check(self.root.stated_path())?;
let jp = crate::path::strict_path::StrictPath::new(
std::sync::Arc::new(self.root.clone()),
validated,
);
Ok(jp.virtualize())
}
#[inline]
pub(crate) fn path(&self) -> &Path {
self.root.path()
}
#[inline]
pub fn interop_path(&self) -> &std::ffi::OsStr {
self.root.interop_path()
}
#[inline]
pub fn exists(&self) -> bool {
self.root.exists()
}
#[inline]
pub fn as_unvirtual(&self) -> &PathBoundary<Marker> {
&self.root
}
#[inline]
pub fn unvirtual(self) -> PathBoundary<Marker> {
self.root
}
}
impl<Marker> std::fmt::Display for VirtualRoot<Marker> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let display = self.path().display();
write!(f, "{display}")
}
}
impl<Marker> AsRef<Path> for VirtualRoot<Marker> {
fn as_ref(&self) -> &Path {
self.path()
}
}
impl<Marker> std::fmt::Debug for VirtualRoot<Marker> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("VirtualRoot")
.field("root", &self.path())
.field("marker", &std::any::type_name::<Marker>())
.finish()
}
}
impl<Marker> Eq for VirtualRoot<Marker> {}
impl<M1, M2> PartialEq<VirtualRoot<M2>> for VirtualRoot<M1> {
#[inline]
fn eq(&self, other: &VirtualRoot<M2>) -> bool {
self.path() == other.path()
}
}
impl<Marker> std::hash::Hash for VirtualRoot<Marker> {
#[inline]
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.path().hash(state);
}
}
impl<Marker> PartialOrd for VirtualRoot<Marker> {
#[inline]
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
Some(self.cmp(other))
}
}
impl<Marker> Ord for VirtualRoot<Marker> {
#[inline]
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
self.path().cmp(other.path())
}
}
impl<M1, M2> PartialEq<crate::PathBoundary<M2>> for VirtualRoot<M1> {
#[inline]
fn eq(&self, other: &crate::PathBoundary<M2>) -> bool {
self.path() == other.path()
}
}
impl<Marker> PartialEq<std::path::Path> for VirtualRoot<Marker> {
#[inline]
fn eq(&self, other: &std::path::Path) -> bool {
let other_str = other.to_string_lossy();
#[cfg(windows)]
let other_normalized = other_str.replace('\\', "/");
#[cfg(not(windows))]
let other_normalized = other_str.to_string();
let normalized_other = if other_normalized.starts_with('/') {
other_normalized
} else {
format!("/{other_normalized}")
};
"/" == normalized_other
}
}
impl<Marker> PartialEq<std::path::PathBuf> for VirtualRoot<Marker> {
#[inline]
fn eq(&self, other: &std::path::PathBuf) -> bool {
self.eq(other.as_path())
}
}
impl<Marker> PartialEq<&std::path::Path> for VirtualRoot<Marker> {
#[inline]
fn eq(&self, other: &&std::path::Path) -> bool {
self.eq(*other)
}
}
impl<Marker> PartialOrd<std::path::Path> for VirtualRoot<Marker> {
#[inline]
fn partial_cmp(&self, other: &std::path::Path) -> Option<std::cmp::Ordering> {
let other_str = other.to_string_lossy();
if other_str.is_empty() {
return Some(std::cmp::Ordering::Greater);
}
#[cfg(windows)]
let other_normalized = other_str.replace('\\', "/");
#[cfg(not(windows))]
let other_normalized = other_str.to_string();
let normalized_other = if other_normalized.starts_with('/') {
other_normalized
} else {
format!("/{other_normalized}")
};
Some("/".cmp(&normalized_other))
}
}
impl<Marker> PartialOrd<&std::path::Path> for VirtualRoot<Marker> {
#[inline]
fn partial_cmp(&self, other: &&std::path::Path) -> Option<std::cmp::Ordering> {
self.partial_cmp(*other)
}
}
impl<Marker> PartialOrd<std::path::PathBuf> for VirtualRoot<Marker> {
#[inline]
fn partial_cmp(&self, other: &std::path::PathBuf) -> Option<std::cmp::Ordering> {
self.partial_cmp(other.as_path())
}
}
impl<Marker: Default> std::str::FromStr for VirtualRoot<Marker> {
type Err = crate::StrictPathError;
#[inline]
fn from_str(path: &str) -> std::result::Result<Self, Self::Err> {
Self::try_new_create(path)
}
}
pub struct VirtualRootReadDir<'a, Marker> {
inner: std::fs::ReadDir,
vroot: &'a VirtualRoot<Marker>,
}
impl<Marker> std::fmt::Debug for VirtualRootReadDir<'_, Marker> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("VirtualRootReadDir")
.field("vroot", &"/")
.finish_non_exhaustive()
}
}
impl<Marker: Clone> Iterator for VirtualRootReadDir<'_, Marker> {
type Item = std::io::Result<crate::path::virtual_path::VirtualPath<Marker>>;
fn next(&mut self) -> Option<Self::Item> {
match self.inner.next()? {
Ok(entry) => {
let file_name = entry.file_name();
match self.vroot.virtual_join(file_name) {
Ok(virtual_path) => Some(Ok(virtual_path)),
Err(e) => Some(Err(std::io::Error::new(std::io::ErrorKind::InvalidData, e))),
}
}
Err(e) => Some(Err(e)),
}
}
}