use std::env;
use std::ffi;
use std::fmt;
use std::io;
use std::path::{Component, PrefixComponent};
use std_prelude::*;
use super::{Error, PathMut, PathOps, Result};
fn make_verbatim_prefix(prefix: &PrefixComponent<'_>) -> Result<PathBuf> {
let path_prefix = Path::new(prefix.as_os_str());
if prefix.kind().is_verbatim() {
Ok(path_prefix.to_path_buf())
} else {
let res = path_prefix
.canonicalize()
.map_err(|e| Error::new(e, "canonicalizing", path_prefix.to_path_buf().into()))?;
Ok(res)
}
}
fn pop_or_error(path: &mut PathBuf) -> ::std::result::Result<(), io::Error> {
if path.pop() {
Ok(())
} else {
Err(io::Error::new(io::ErrorKind::NotFound, ".. consumed root"))
}
}
#[derive(Clone, Eq, Hash, PartialEq, PartialOrd, Ord)]
pub struct PathAbs(pub(crate) Arc<PathBuf>);
impl PathAbs {
pub fn new<P: AsRef<Path>>(path: P) -> Result<PathAbs> {
let path = Arc::new(path.as_ref().to_path_buf());
let mut res = PathBuf::new();
fn maybe_init_res(res: &mut PathBuf, resolvee: Arc<PathBuf>) -> Result<()> {
if !res.as_os_str().is_empty() {
return Ok(());
}
let cwd = env::current_dir().map_err(|e| {
Error::new(e, "getting current_dir while resolving absolute", resolvee)
})?;
*res = cwd
.canonicalize()
.map_err(|e| Error::new(e, "canonicalizing", cwd.into()))?;
Ok(())
};
for each in path.components() {
match each {
Component::Prefix(p) => {
res = make_verbatim_prefix(&p)?;
}
Component::RootDir => {
if cfg!(windows) {
maybe_init_res(&mut res, path.clone())?;
res.push(each);
} else {
res = Path::new(&each).to_path_buf();
}
}
Component::CurDir => (),
Component::ParentDir => {
maybe_init_res(&mut res, path.clone())?;
pop_or_error(&mut res)
.map_err(|e| Error::new(e, "resolving absolute", path.clone()))?;
}
Component::Normal(c) => {
maybe_init_res(&mut res, path.clone())?;
res.push(c);
}
}
}
Ok(PathAbs(Arc::new(res)))
}
pub fn new_unchecked<P: Into<Arc<PathBuf>>>(path: P) -> PathAbs {
PathAbs(path.into())
}
pub fn as_path(&self) -> &Path {
self.as_ref()
}
}
impl fmt::Debug for PathAbs {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.0.fmt(f)
}
}
impl PathMut for PathAbs {
fn append<P: AsRef<Path>>(&mut self, path: P) -> Result<()> {
self.0.append(path)
}
fn pop_up(&mut self) -> Result<()> {
self.0.pop_up()
}
fn truncate_to_root(&mut self) {
self.0.truncate_to_root()
}
fn set_file_name<S: AsRef<ffi::OsStr>>(&mut self, file_name: S) {
self.0.set_file_name(file_name)
}
fn set_extension<S: AsRef<ffi::OsStr>>(&mut self, extension: S) -> bool {
self.0.set_extension(extension)
}
}
impl PathOps for PathAbs {
type Output = PathAbs;
fn concat<P: AsRef<Path>>(&self, path: P) -> Result<Self::Output> {
Ok(PathAbs(self.0.concat(path)?))
}
fn join<P: AsRef<Path>>(&self, path: P) -> Self::Output {
let buf = Path::join(self.as_path(), path);
Self::Output::new_unchecked(buf)
}
fn with_file_name<S: AsRef<ffi::OsStr>>(&self, file_name: S) -> Self::Output {
PathAbs(self.0.with_file_name(file_name))
}
fn with_extension<S: AsRef<ffi::OsStr>>(&self, extension: S) -> Self::Output {
PathAbs(self.0.with_extension(extension))
}
}
impl AsRef<ffi::OsStr> for PathAbs {
fn as_ref(&self) -> &std::ffi::OsStr {
self.0.as_ref().as_ref()
}
}
impl AsRef<Path> for PathAbs {
fn as_ref(&self) -> &Path {
self.0.as_ref()
}
}
impl AsRef<PathBuf> for PathAbs {
fn as_ref(&self) -> &PathBuf {
self.0.as_ref()
}
}
impl Borrow<Path> for PathAbs {
fn borrow(&self) -> &Path {
self.as_ref()
}
}
impl Borrow<PathBuf> for PathAbs {
fn borrow(&self) -> &PathBuf {
self.as_ref()
}
}
impl<'a> Borrow<Path> for &'a PathAbs {
fn borrow(&self) -> &Path {
self.as_ref()
}
}
impl<'a> Borrow<PathBuf> for &'a PathAbs {
fn borrow(&self) -> &PathBuf {
self.as_ref()
}
}
impl From<PathAbs> for Arc<PathBuf> {
fn from(path: PathAbs) -> Arc<PathBuf> {
path.0
}
}
impl From<PathAbs> for PathBuf {
fn from(path: PathAbs) -> PathBuf {
match Arc::try_unwrap(path.0) {
Ok(p) => p,
Err(inner) => inner.as_ref().clone(),
}
}
}