use std::fmt;
use std::fs;
use std::io;
use std::env;
use std::ffi::OsStr;
use std::path::{Component, Components, Prefix};
use std_prelude::*;
use super::{Error, Result};
use abs::PathAbs;
use dir::{ListDir, PathDir};
pub fn current_dir(resolving: &PathArc) -> Result<PathArc> {
let cwd = env::current_dir().map_err(|e| {
Error::new(
e,
"getting current_dir while resolving absolute",
resolving.clone(),
)
})?;
Ok(PathArc::from(cwd))
}
#[derive(Clone, Eq, Hash, PartialEq, PartialOrd, Ord)]
pub struct PathArc(pub(crate) Arc<PathBuf>);
impl PathArc {
pub fn new<P: AsRef<Path>>(path: P) -> PathArc {
PathArc::from(path.as_ref().to_path_buf())
}
pub fn join<P: AsRef<Path>>(&self, path: P) -> PathArc {
PathArc::from(self.0.join(path))
}
pub fn with_file_name<P: AsRef<OsStr>>(&self, file_name: P) -> PathArc {
PathArc::from(self.0.with_file_name(file_name))
}
pub fn with_extension<P: AsRef<OsStr>>(&self, extension: P) -> PathArc {
PathArc::from(self.0.with_extension(extension))
}
pub fn metadata(&self) -> Result<fs::Metadata> {
self.0
.metadata()
.map_err(|err| Error::new(err, "getting metadata of", self.clone()))
}
pub fn symlink_metadata(&self) -> Result<fs::Metadata> {
self.0
.symlink_metadata()
.map_err(|err| Error::new(err, "getting symlink_metadata of", self.clone()))
}
pub fn canonicalize(&self) -> Result<PathAbs> {
let abs = self.0
.canonicalize()
.map_err(|err| Error::new(err, "canonicalizing", self.clone()))?;
Ok(PathAbs(PathArc::from(abs)))
}
pub fn read_link(&self) -> Result<PathArc> {
let path = self.0
.read_link()
.map_err(|err| Error::new(err, "reading link", self.clone()))?;
Ok(PathArc::from(path))
}
pub fn read_dir(&self) -> Result<ListDir> {
let dir = PathDir::new(self)?;
dir.list()
}
pub fn as_path(&self) -> &Path {
self.as_ref()
}
pub fn absolute(&self) -> Result<PathAbs> {
let mut components = self.components();
let mut stack: Vec<OsString> = Vec::new();
macro_rules! pop_stack { [] => {{
if let None = stack.pop() {
return Err(Error::new(
io::Error::new(io::ErrorKind::NotFound, ".. consumed root"),
"resolving absolute",
self.clone(),
));
}
}}}
handle_prefix(self, &mut stack, &mut components, false)?;
for component in components {
match component {
Component::CurDir => { }
Component::Prefix(_) => unreachable!(),
Component::RootDir => {
if cfg!(unix) {
unreachable!("root is already handled on unix");
}
stack.push(to_os(component));
}
Component::ParentDir => pop_stack!(),
Component::Normal(_) => stack.push(to_os(component)),
}
}
if stack.is_empty() {
return Err(Error::new(
io::Error::new(io::ErrorKind::NotFound, "resolving resulted in empty path"),
"resolving absolute",
self.clone(),
));
}
Ok(PathAbs(PathArc(Arc::new(PathBuf::from_iter(stack)))))
}
}
impl fmt::Debug for PathArc {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.0.fmt(f)
}
}
impl AsRef<Path> for PathArc {
fn as_ref(&self) -> &Path {
self.0.as_path()
}
}
impl AsRef<PathBuf> for PathArc {
fn as_ref(&self) -> &PathBuf {
&self.0
}
}
impl Borrow<Path> for PathArc {
fn borrow(&self) -> &Path {
self.as_ref()
}
}
impl Borrow<PathBuf> for PathArc {
fn borrow(&self) -> &PathBuf {
self.as_ref()
}
}
impl<'a> Borrow<Path> for &'a PathArc {
fn borrow(&self) -> &Path {
self.as_ref()
}
}
impl<'a> Borrow<PathBuf> for &'a PathArc {
fn borrow(&self) -> &PathBuf {
self.as_ref()
}
}
impl Deref for PathArc {
type Target = PathBuf;
fn deref(&self) -> &PathBuf {
&self.0
}
}
impl From<PathBuf> for PathArc {
fn from(path: PathBuf) -> PathArc {
PathArc(Arc::new(path))
}
}
impl Into<PathBuf> for PathArc {
fn into(self) -> PathBuf {
match Arc::try_unwrap(self.0) {
Ok(p) => p,
Err(inner) => inner.as_ref().clone(),
}
}
}
fn to_os(c: Component) -> OsString {
c.as_os_str().to_os_string()
}
fn handle_prefix(
resolving: &PathArc,
stack: &mut Vec<OsString>,
components: &mut Components,
recursing: bool,
) -> Result<()> {
macro_rules! pop_stack { [] => {{
if let None = stack.pop() {
return Err(Error::new(
io::Error::new(io::ErrorKind::NotFound, ".. consumed root"),
"resolving absolute",
resolving.clone(),
));
}
}}}
loop {
assert_eq!(stack.len(), 0, "{:?}", stack);
let component = match components.next() {
None => break,
Some(c) => c,
};
match component {
Component::CurDir => {
assert!(!recursing);
continue;
}
Component::Prefix(prefix) => {
assert!(!cfg!(unix), "Component::Prefix in unix");
match prefix.kind() {
Prefix::Disk(_) | Prefix::UNC(_, _) => {
let c = PathArc::new(component.as_os_str()).canonicalize()?;
stack.extend(c.components().map(to_os));
}
_ => {
stack.push(to_os(component));
}
}
}
Component::RootDir => {
if cfg!(windows) {
assert!(!recursing);
let cwd = current_dir(resolving)?;
handle_prefix(resolving, stack, &mut cwd.components(), true)?;
{
let first = Path::new(&stack[0]).components().next().unwrap();
if let Component::Prefix(prefix) = first {
if let Prefix::DeviceNS(_) = prefix.kind() {
} else if !prefix.kind().is_verbatim() {
panic!(
"First item kind is neither verbatim nor DeviceNs: {:?}",
stack
)
}
} else {
panic!("First item is not a Prefix on windows: {:?}", stack)
}
}
}
stack.push(to_os(component));
}
Component::ParentDir | Component::Normal(_) => {
assert!(!recursing);
let cwd = current_dir(resolving)?;
let mut cwd_components = cwd.components();
handle_prefix(resolving, stack, &mut cwd_components, true)?;
stack.extend(cwd_components.map(to_os));
match component {
Component::ParentDir => pop_stack!(),
Component::Normal(_) => stack.push(to_os(component)),
_ => unreachable!(),
}
}
}
break;
}
Ok(())
}
#[test]
fn test_prefix_windows() {
fn f<P: AsRef<Path>>(p: P) -> Result<PathAbs> {
PathArc::new(p).absolute()
}
assert!(f(r"\\?\C:\blah\blah").is_ok());
assert!(f(r"\blah\blah").is_ok());
assert!(f(r"C:\blah\blah").is_ok());
}