use anyhow::Result;
use std::{
convert::TryFrom,
fmt,
ops::Deref,
path::{Path, PathBuf},
};
#[derive(PartialOrd, Ord, Eq, PartialEq, Hash, Clone)]
pub struct AbsPath {
inner: PathBuf,
}
impl fmt::Debug for AbsPath {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.inner.display())
}
}
impl TryFrom<PathBuf> for AbsPath {
type Error = anyhow::Error;
fn try_from(p: PathBuf) -> Result<Self> {
Ok(AbsPath {
inner: p.canonicalize()?,
})
}
}
impl TryFrom<&Path> for AbsPath {
type Error = anyhow::Error;
fn try_from(p: &Path) -> Result<Self> {
Ok(AbsPath {
inner: PathBuf::from(p).canonicalize()?,
})
}
}
impl TryFrom<&String> for AbsPath {
type Error = anyhow::Error;
fn try_from(p: &String) -> Result<Self> {
Ok(AbsPath {
inner: PathBuf::from(p).canonicalize()?,
})
}
}
impl TryFrom<String> for AbsPath {
type Error = anyhow::Error;
fn try_from(p: String) -> Result<Self> {
Ok(AbsPath {
inner: PathBuf::from(p).canonicalize()?,
})
}
}
impl TryFrom<&str> for AbsPath {
type Error = anyhow::Error;
fn try_from(p: &str) -> Result<Self> {
Ok(AbsPath {
inner: PathBuf::from(p).canonicalize()?,
})
}
}
impl Deref for AbsPath {
type Target = Path;
fn deref(&self) -> &Self::Target {
self.inner.as_path()
}
}
impl AsRef<Path> for AbsPath {
fn as_ref(&self) -> &Path {
self.inner.as_path()
}
}
pub fn path_relative_from(path: &Path, base: &Path) -> Option<PathBuf> {
use std::path::Component;
if path.is_absolute() != base.is_absolute() {
if path.is_absolute() {
Some(PathBuf::from(path))
} else {
None
}
} else {
let mut ita = path.components();
let mut itb = base.components();
let mut comps: Vec<Component> = vec![];
loop {
match (ita.next(), itb.next()) {
(None, None) => break,
(Some(a), None) => {
comps.push(a);
comps.extend(ita.by_ref());
break;
}
(None, _) => comps.push(Component::ParentDir),
(Some(a), Some(b)) if comps.is_empty() && a == b => (),
(Some(a), Some(b)) if b == Component::CurDir => comps.push(a),
(Some(_), Some(b)) if b == Component::ParentDir => return None,
(Some(a), Some(_)) => {
comps.push(Component::ParentDir);
for _ in itb {
comps.push(Component::ParentDir);
}
comps.push(a);
comps.extend(ita.by_ref());
break;
}
}
}
Some(comps.iter().map(|c| c.as_os_str()).collect())
}
}
pub fn get_display_path(path: &str, current_dir: &Path) -> String {
let abs_path = AbsPath::try_from(path);
match abs_path {
Ok(abs_path) => {
let relative_path = path_relative_from(&abs_path, current_dir).unwrap();
relative_path.display().to_string()
}
Err(_) => path.to_string(),
}
}