use std::fmt;
use std::path::{Path, PathBuf};
use std::borrow::Borrow;
use std::sync::Arc;
use serde::de::{Deserialize, Deserializer, Error};
#[derive(PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Clone)]
pub struct VPath(Arc<PathBuf>);
#[derive(Fail, Debug)]
#[fail(display="path is not a valid virtual path")]
pub struct PathError;
impl VPath {
pub fn key(&self) -> &str {
self.0.iter().nth(1).and_then(|x| x.to_str())
.expect("valid virtual path")
}
pub fn key_vpath(&self) -> VPath {
self.0.iter().nth(1).and_then(|x| x.to_str())
.map(|x| VPath::from(format!("/{}", x)))
.expect("valid virtual path")
}
pub fn level(&self) -> usize {
self.0.iter().count() - 2
}
pub fn parent_rel(&self) -> &Path {
assert!(self.level() > 0);
self.0.strip_prefix("/").ok().and_then(|x| x.parent())
.expect("valid virtual path")
}
pub fn parent(&self) -> VPath {
assert!(self.level() > 0);
let parent = self.0.parent().expect("valid virtual path");
VPath(Arc::new(parent.to_path_buf()))
}
pub fn suffix(&self) -> &Path {
let mut names = self.0.iter();
names.next().expect("valid virtual path"); names.next().expect("valid virtual path"); names.as_path()
}
pub fn final_name(&self) -> &str {
assert!(self.level() > 0);
self.0.file_name().and_then(|x| x.to_str())
.expect("valid virtual path")
}
pub fn join<P: AsRef<Path>>(&self, path: P) -> VPath {
use std::path::Component::Normal;
let path = path.as_ref();
assert!(path != Path::new(""));
assert!(path.components().all(|x| matches!(x, Normal(_))));
VPath(Arc::new(self.0.join(path)))
}
pub fn from<T: Into<PathBuf>>(t: T) -> VPath {
let buf = t.into();
assert!(buf.is_absolute());
assert!(buf != Path::new("/"));
VPath(Arc::new(buf))
}
pub fn try_from<T: Into<PathBuf>>(t: T) -> Result<VPath, PathError> {
let buf = t.into();
if !buf.is_absolute() || buf == Path::new("/") {
return Err(PathError);
}
Ok(VPath(Arc::new(buf)))
}
pub fn matches_basedir(&self, base_dir: &VPath) -> bool {
assert!(self.level() > 0);
let parent = self.0.parent().expect("valid virtual path");
return parent == base_dir.0.as_path();
}
}
impl Borrow<Path> for VPath {
fn borrow(&self) -> &Path {
(&*self.0).borrow()
}
}
impl AsRef<Path> for VPath {
fn as_ref(&self) -> &Path {
self.0.as_ref()
}
}
impl<'de> Deserialize<'de> for VPath {
fn deserialize<D>(deserializer: D) -> Result<VPath, D::Error>
where D: Deserializer<'de>
{
let s = String::deserialize(deserializer)?;
if !check_path(&s) {
return Err(D::Error::custom("virtual path must be absolute \
and must contain at least two components"));
}
Ok(VPath(Arc::new(s.into())))
}
}
fn check_path(path: &str) -> bool {
use std::path::Component::*;
let path = Path::new(path);
let mut piter = path.components();
if piter.next() != Some(RootDir) {
return false;
}
let mut num = 0;
for cmp in piter {
if matches!(cmp, Normal(_)) {
num += 1;
} else {
return false;
}
}
return num >= 1;
}
impl fmt::Display for VPath {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.0.display().fmt(f)
}
}
impl fmt::Debug for VPath {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "v{:?}", self.0)
}
}