use crate::ext::anyhow::{bail, ensure, Context, Result};
use std::path::{Path, PathBuf};
pub trait PathExt {
fn with<P: AsRef<Path>>(&self, append: P) -> PathBuf;
fn relative_to(&self, to: impl AsRef<Path>) -> Option<PathBuf>;
fn rebase(&self, src_root: &PathBuf, dest_root: &PathBuf) -> Result<PathBuf>;
fn to_canonicalized(&self) -> Result<PathBuf>;
}
impl PathExt for Path {
fn rebase(&self, src_root: &PathBuf, dest_root: &PathBuf) -> Result<PathBuf> {
self.to_path_buf().rebase(src_root, dest_root)
}
fn relative_to(&self, to: impl AsRef<Path>) -> Option<PathBuf> {
self.to_path_buf().relative_to(to)
}
fn to_canonicalized(&self) -> Result<PathBuf> {
self.to_path_buf().to_canonicalized()
}
fn with<P: AsRef<Path>>(&self, append: P) -> PathBuf {
self.to_path_buf().with(append)
}
}
pub trait PathBufExt: PathExt {
fn without_last(self) -> PathBuf;
}
impl PathBufExt for PathBuf {
fn without_last(mut self) -> PathBuf {
self.pop();
self
}
}
impl PathExt for PathBuf {
fn with<P: AsRef<Path>>(&self, append: P) -> PathBuf {
let mut new = self.clone();
new.push(append);
new
}
fn relative_to(&self, to: impl AsRef<Path>) -> Option<PathBuf> {
let root = to.as_ref();
if self.is_absolute() && self.starts_with(root) {
let len = root.components().count();
Some(self.components().skip(len).collect())
} else {
None
}
}
fn rebase(&self, src_root: &PathBuf, dest_root: &PathBuf) -> Result<PathBuf>
where
Self: Sized,
{
ensure!(src_root.is_absolute(), "Not canonicalized: {src_root:?}");
ensure!(dest_root.is_absolute(), "Not canonicalized: {dest_root:?}");
if let Some(rel) = self.relative_to(src_root) {
Ok(dest_root.with(rel))
} else {
bail!("Could not rebase {self:?} from {src_root:?} to {dest_root:?}")
}
}
fn to_canonicalized(&self) -> Result<PathBuf>
where
Self: Sized,
{
self.canonicalize()
.context(format!("Could not canonicalize {:?}", self))
}
}