#![cfg_attr(target_os = "wasi",
feature(wasi_ext))]
#[cfg(feature = "serialize")]
extern crate serde;
#[macro_use]
#[cfg(feature = "serialize")]
extern crate serde_derive;
#[cfg(feature = "serialize")]
extern crate stfu8;
#[macro_use]
#[cfg(test)]
extern crate pretty_assertions;
#[cfg(test)]
extern crate regex;
#[cfg(test)]
extern crate serde_json;
#[cfg(test)]
extern crate tempdir;
use std::error;
use std::ffi;
use std::fmt;
use std::fs;
use std::io;
use std::path::{self, Component, Components};
use std_prelude::*;
mod abs;
mod dir;
mod edit;
mod file;
pub mod open;
mod read;
#[cfg(feature = "serialize")]
pub mod ser;
mod ty;
mod write;
pub use crate::abs::PathAbs;
pub use crate::dir::{ListDir, PathDir};
pub use crate::file::PathFile;
#[cfg(feature = "serialize")]
pub use crate::ser::PathSer;
pub use crate::ty::PathType;
pub use crate::edit::FileEdit;
pub use crate::read::FileRead;
pub use crate::write::FileWrite;
pub type Result<T> = ::std::result::Result<T, Error>;
pub struct Error {
io_err: io::Error,
action: String,
path: Arc<PathBuf>,
}
impl Error {
pub fn new(io_err: io::Error, action: &str, path: Arc<PathBuf>) -> Error {
Error {
io_err,
action: action.into(),
path,
}
}
}
impl fmt::Debug for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Error<{}>", self)
}
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"{} when {} {}",
self.io_err,
self.action,
self.path.display()
)
}
}
impl Error {
pub fn path(&self) -> &Path {
self.path.as_ref()
}
pub fn io_error(&self) -> &io::Error {
&self.io_err
}
pub fn action(&self) -> &str {
&self.action
}
}
impl error::Error for Error {
fn description(&self) -> &str {
self.io_err.description()
}
fn cause(&self) -> Option<&dyn error::Error> {
Some(&self.io_err)
}
}
impl From<Error> for io::Error {
fn from(err: Error) -> io::Error {
io::Error::new(err.io_err.kind(), err)
}
}
pub trait PathInfo {
fn as_path(&self) -> &Path;
fn to_arc_pathbuf(&self) -> Arc<PathBuf>;
fn as_os_str(&self) -> &ffi::OsStr {
Path::as_os_str(self.as_path())
}
fn to_str(&self) -> Option<&str> {
Path::to_str(self.as_path())
}
fn to_string_lossy(&self) -> Cow<'_, str> {
Path::to_string_lossy(self.as_path())
}
fn is_absolute(&self) -> bool {
Path::is_absolute(self.as_path())
}
fn is_relative(&self) -> bool {
Path::is_relative(self.as_path())
}
fn has_root(&self) -> bool {
Path::has_root(self.as_path())
}
fn ancestors(&self) -> path::Ancestors<'_> {
Path::ancestors(self.as_path())
}
fn file_name(&self) -> Option<&ffi::OsStr> {
Path::file_name(self.as_path())
}
fn strip_prefix<P>(&self, base: P) -> std::result::Result<&Path, path::StripPrefixError>
where
P: AsRef<Path>,
{
Path::strip_prefix(self.as_path(), base)
}
fn starts_with<P: AsRef<Path>>(&self, base: P) -> bool {
Path::starts_with(self.as_path(), base)
}
fn ends_with<P: AsRef<Path>>(&self, base: P) -> bool {
Path::ends_with(self.as_path(), base)
}
fn file_stem(&self) -> Option<&ffi::OsStr> {
Path::file_stem(self.as_path())
}
fn extension(&self) -> Option<&ffi::OsStr> {
Path::extension(self.as_path())
}
fn components(&self) -> Components<'_> {
Path::components(self.as_path())
}
fn iter(&self) -> path::Iter<'_> {
Path::iter(self.as_path())
}
fn display(&self) -> path::Display<'_> {
Path::display(self.as_path())
}
fn metadata(&self) -> Result<fs::Metadata> {
Path::metadata(self.as_path())
.map_err(|err| Error::new(err, "getting metadata of", self.to_arc_pathbuf()))
}
fn symlink_metadata(&self) -> Result<fs::Metadata> {
Path::symlink_metadata(self.as_path())
.map_err(|err| Error::new(err, "getting symlink metadata of", self.to_arc_pathbuf()))
}
fn exists(&self) -> bool {
Path::exists(self.as_path())
}
fn is_file(&self) -> bool {
Path::is_file(self.as_path())
}
fn is_dir(&self) -> bool {
Path::is_dir(self.as_path())
}
fn read_link(&self) -> Result<PathBuf> {
Path::read_link(self.as_path())
.map_err(|err| Error::new(err, "reading link target of", self.to_arc_pathbuf()))
}
fn canonicalize(&self) -> Result<PathAbs> {
Path::canonicalize(self.as_path())
.map(|path| PathAbs(path.into()))
.map_err(|err| Error::new(err, "canonicalizing", self.to_arc_pathbuf()))
}
fn parent(&self) -> Result<&Path> {
let parent_path = Path::parent(self.as_path());
if let Some(p) = parent_path {
Ok(p)
} else {
Err(Error::new(
io::Error::new(io::ErrorKind::NotFound, "path has no parent"),
"truncating to parent",
self.to_arc_pathbuf(),
))
}
}
}
impl<T> PathInfo for T
where
T: Clone + Borrow<PathBuf> + Into<Arc<PathBuf>>,
{
fn as_path(&self) -> &Path {
PathBuf::as_path(self.borrow())
}
fn to_arc_pathbuf(&self) -> Arc<PathBuf> {
self.clone().into()
}
}
impl PathInfo for Path {
fn as_path(&self) -> &Path {
&self
}
fn to_arc_pathbuf(&self) -> Arc<PathBuf> {
self.to_path_buf().into()
}
}
pub trait PathMut: PathInfo {
fn append<P: AsRef<Path>>(&mut self, path: P) -> Result<()>;
fn pop_up(&mut self) -> Result<()>;
fn truncate_to_root(&mut self);
fn set_file_name<S: AsRef<ffi::OsStr>>(&mut self, file_name: S);
fn set_extension<S: AsRef<ffi::OsStr>>(&mut self, extension: S) -> bool;
}
impl PathMut for PathBuf {
fn append<P: AsRef<Path>>(&mut self, path: P) -> Result<()> {
for each in path.as_ref().components() {
match each {
Component::Normal(c) => self.push(c),
Component::CurDir => (), Component::Prefix(_) => {
return Err(Error::new(
io::Error::new(io::ErrorKind::Other, "appended path has a prefix"),
"appending path",
path.as_ref().to_path_buf().into(),
));
}
Component::RootDir => (), Component::ParentDir => self.pop_up()?,
}
}
Ok(())
}
fn pop_up(&mut self) -> Result<()> {
fn pop_parent_components(p: &mut PathBuf) -> usize {
let mut cur_dirs: usize = 0;
let mut parents: usize = 0;
let mut components = p.components();
while let Some(c) = components.next_back() {
match c {
Component::CurDir => cur_dirs += 1,
Component::ParentDir => parents += 1,
_ => break,
}
}
for _ in 0..(cur_dirs + parents) {
p.pop();
}
parents
}
let mut ending_parents = 0;
loop {
ending_parents += pop_parent_components(self);
if ending_parents == 0 || self.file_name().is_none() {
break;
} else {
self.pop();
ending_parents -= 1;
}
}
if self.pop() {
} else if self.has_root() {
return Err(Error::new(
io::Error::new(io::ErrorKind::NotFound, "cannot get parent of root path"),
"truncating to parent",
self.clone().into(),
));
} else {
self.push("..")
}
for _ in 0..ending_parents {
self.push("..")
}
Ok(())
}
fn truncate_to_root(&mut self) {
let mut res = PathBuf::new();
for component in self.components().take(2) {
match component {
Component::Prefix(_) | Component::RootDir => res.push(component),
_ => break,
}
}
*self = res;
}
fn set_file_name<S: AsRef<ffi::OsStr>>(&mut self, file_name: S) {
self.set_file_name(file_name)
}
fn set_extension<S: AsRef<ffi::OsStr>>(&mut self, extension: S) -> bool {
self.set_extension(extension)
}
}
impl PathMut for Arc<PathBuf> {
fn append<P: AsRef<Path>>(&mut self, path: P) -> Result<()> {
Arc::make_mut(self).append(path)
}
fn pop_up(&mut self) -> Result<()> {
Arc::make_mut(self).pop_up()
}
fn truncate_to_root(&mut self) {
Arc::make_mut(self).truncate_to_root()
}
fn set_file_name<S: AsRef<ffi::OsStr>>(&mut self, file_name: S) {
Arc::make_mut(self).set_file_name(file_name)
}
fn set_extension<S: AsRef<ffi::OsStr>>(&mut self, extension: S) -> bool {
Arc::make_mut(self).set_extension(extension)
}
}
pub trait PathOps: PathInfo {
type Output: PathOps;
fn concat<P: AsRef<Path>>(&self, path: P) -> Result<Self::Output>;
fn join<P: AsRef<Path>>(&self, path: P) -> Self::Output;
fn with_file_name<S: AsRef<ffi::OsStr>>(&self, file_name: S) -> Self::Output;
fn with_extension<S: AsRef<ffi::OsStr>>(&self, extension: S) -> Self::Output;
}
impl PathOps for Path {
type Output = PathBuf;
fn concat<P: AsRef<Path>>(&self, path: P) -> Result<Self::Output> {
let mut res = self.to_owned();
res.append(path)?;
Ok(res)
}
fn join<P: AsRef<Path>>(&self, path: P) -> Self::Output {
Path::join(self, path)
}
fn with_file_name<S: AsRef<ffi::OsStr>>(&self, file_name: S) -> Self::Output {
let mut res = self.to_owned();
res.set_file_name(file_name);
res
}
fn with_extension<S: AsRef<ffi::OsStr>>(&self, extension: S) -> Self::Output {
let mut res = self.to_owned();
res.set_extension(extension);
res
}
}
impl PathOps for PathBuf {
type Output = PathBuf;
fn concat<P: AsRef<Path>>(&self, path: P) -> Result<Self::Output> {
self.as_path().concat(path)
}
fn join<P: AsRef<Path>>(&self, path: P) -> Self::Output {
Path::join(self, path)
}
fn with_file_name<S: AsRef<ffi::OsStr>>(&self, file_name: S) -> Self::Output {
self.as_path().with_file_name(file_name)
}
fn with_extension<S: AsRef<ffi::OsStr>>(&self, extension: S) -> Self::Output {
self.as_path().with_extension(extension)
}
}
impl PathOps for Arc<PathBuf> {
type Output = Arc<PathBuf>;
fn concat<P: AsRef<Path>>(&self, path: P) -> Result<Self::Output> {
let mut res = self.clone();
Arc::make_mut(&mut res).append(path)?;
Ok(res)
}
fn join<P: AsRef<Path>>(&self, path: P) -> Self::Output {
let buf = Path::join(self, path);
Arc::new(buf)
}
fn with_file_name<S: AsRef<ffi::OsStr>>(&self, file_name: S) -> Self::Output {
let mut res = self.clone();
Arc::make_mut(&mut res).set_file_name(file_name);
res
}
fn with_extension<S: AsRef<ffi::OsStr>>(&self, extension: S) -> Self::Output {
let mut res = self.clone();
Arc::make_mut(&mut res).set_extension(extension);
res
}
}
#[cfg(test)]
mod tests {
use regex::{self, Regex};
use tempdir::TempDir;
use super::*;
macro_rules! assert_match {
($re: expr, $err: expr) => {{
let re = Regex::new(&$re).unwrap();
let err = $err.to_string();
assert!(
re.is_match(&err),
"\nGot Err : {:?}\nMatching against: {:?}",
err.to_string(),
$re
);
}};
}
fn escape<P: AsRef<Path>>(path: P) -> String {
regex::escape(&format!("{}", path.as_ref().display()))
}
#[test]
fn sanity_errors() {
let tmp_dir = TempDir::new("example").expect("create temp dir");
let tmp_abs = PathDir::new(tmp_dir.path()).expect("tmp_abs");
{
let foo_path = tmp_abs.concat("foo.txt").expect("path foo.txt");
let foo = PathFile::create(foo_path).expect("create foo.txt");
foo.clone().remove().unwrap();
let pat = if cfg!(unix) {
format!(
r"No such file or directory \(os error \d+\) when opening {}",
escape(&foo)
)
} else {
format!(
r"The system cannot find the file specified. \(os error \d+\) when opening {}",
escape(&foo)
)
};
assert_match!(pat, foo.open_edit().unwrap_err())
}
}
#[cfg(test)]
mod windows {
use super::*;
#[cfg_attr(windows, test)]
fn _test_pathinfo_parent() {
let p = PathBuf::from(r"C:\foo\bar");
let actual = <PathBuf as PathInfo>::parent(&p).expect("could not find parent?");
let expected = PathBuf::from(r"C:\foo");
assert_eq!(actual, expected);
let p = PathBuf::from(r"C:\");
let actual = <PathBuf as PathInfo>::parent(&p).expect_err("root has a parent?");
assert_eq!(actual.io_error().kind(), io::ErrorKind::NotFound);
assert_eq!(actual.action(), "truncating to parent");
assert_eq!(actual.path(), Path::new(r"C:\"));
}
#[cfg_attr(windows, test)]
fn _test_pathinfo_starts_with() {
let p = PathBuf::from(r"foo\bar");
assert_eq!(
<PathBuf as PathInfo>::starts_with(&p, Path::new("foo")),
true,
);
assert_eq!(
<PathBuf as PathInfo>::starts_with(&p, Path::new("bar")),
false,
);
}
#[cfg_attr(windows, test)]
fn _test_pathinfo_ends_with() {
let p = PathBuf::from(r"foo\bar");
assert_eq!(
<PathBuf as PathInfo>::ends_with(&p, Path::new("foo")),
false,
);
assert_eq!(<PathBuf as PathInfo>::ends_with(&p, Path::new("bar")), true,);
}
#[cfg_attr(windows, test)]
fn _test_pathops_concat() {
let actual = Path::new("foo")
.concat(Path::new("bar"))
.expect("Could not concat paths?");
let expected = PathBuf::from(r"foo\bar");
assert_eq!(actual, expected);
let actual = Path::new("foo")
.concat(Path::new(r"bar\..\baz"))
.expect("Could not concat path with ..?");
let expected = PathBuf::from(r"foo\baz");
assert_eq!(actual, expected);
let actual = Path::new("foo")
.concat("..")
.expect("Could not cancel path with ..?");
let expected = PathBuf::from(r"");
assert_eq!(actual, expected);
let actual = Path::new("foo")
.concat(r"..\..")
.expect("Could not escape prefix with ..?");
let expected = PathBuf::from("../");
assert_eq!(actual, expected);
let actual = Path::new(r"C:\foo")
.concat(r"..\..")
.expect_err("Could escape root with ..?");
assert_eq!(actual.io_error().kind(), io::ErrorKind::NotFound);
assert_eq!(actual.action(), "truncating to parent");
assert_eq!(actual.path(), Path::new(r"C:\"));
let actual = Path::new("foo")
.concat(Path::new(r"\windows\system32"))
.expect("Could not concat path with RootDir?");
let expected = PathBuf::from(r"foo\windows\system32");
assert_eq!(actual, expected);
let actual = Path::new("foo")
.concat(Path::new(r"C:bar"))
.expect_err("Could concat path with prefix?");
assert_eq!(actual.io_error().kind(), io::ErrorKind::Other);
assert_eq!(actual.action(), "appending path");
assert_eq!(actual.path(), Path::new(r"C:bar"));
}
#[cfg_attr(windows, test)]
fn _test_pathmut_append() {
let mut actual = PathBuf::from("foo");
actual
.append(Path::new("bar"))
.expect("Could not append paths?");
let expected = PathBuf::from(r"foo\bar");
assert_eq!(actual, expected);
let mut actual = PathBuf::from("foo");
actual
.append(Path::new(r"bar\..\baz"))
.expect("Could not append path with ..?");
let expected = PathBuf::from(r"foo\baz");
assert_eq!(actual, expected);
let mut actual = PathBuf::from("foo");
actual.append("..").expect("Could not cancel path with ..?");
let expected = PathBuf::from(r"");
assert_eq!(actual, expected);
let mut actual = PathBuf::from("foo");
actual
.append(r"..\..")
.expect("Could not escape prefix with ..?");
let expected = PathBuf::from("../");
assert_eq!(actual, expected);
let actual = PathBuf::from(r"C:\foo")
.append(r"..\..")
.expect_err("Could escape root with ..?");
assert_eq!(actual.io_error().kind(), io::ErrorKind::NotFound);
assert_eq!(actual.action(), "truncating to parent");
assert_eq!(actual.path(), Path::new(r"C:\"));
let mut actual = PathBuf::from("foo");
actual
.append(Path::new(r"\windows\system32"))
.expect("Could not append RootDir to path?");
let expected = PathBuf::from(r"foo\windows\system32");
assert_eq!(actual, expected);
let actual = PathBuf::from("foo")
.append(Path::new(r"C:bar"))
.expect_err("Could append prefix to path?");
assert_eq!(actual.io_error().kind(), io::ErrorKind::Other);
assert_eq!(actual.action(), "appending path");
assert_eq!(actual.path(), Path::new(r"C:bar"));
}
#[cfg_attr(windows, test)]
fn _test_pathmut_pop_up() {
let mut p = PathBuf::from(r"C:\foo\bar");
p.pop_up().expect("could not find parent?");
assert_eq!(p.as_path(), Path::new(r"C:\foo"));
let mut p = PathBuf::from(r"C:\");
let actual = p.pop_up().expect_err("root has a parent?");
assert_eq!(actual.io_error().kind(), io::ErrorKind::NotFound);
assert_eq!(actual.action(), "truncating to parent");
assert_eq!(actual.path(), Path::new(r"C:\"));
}
#[cfg_attr(windows, test)]
fn _test_pathmut_truncate_to_root() {
let mut p = PathBuf::from(r"C:\foo\bar");
p.truncate_to_root();
assert_eq!(p.as_path(), Path::new(r"C:\"));
let mut p = PathBuf::from(r"C:foo");
p.truncate_to_root();
assert_eq!(p.as_path(), Path::new(r"C:"));
let mut p = PathBuf::from(r"\foo");
p.truncate_to_root();
assert_eq!(p.as_path(), Path::new(r"\"));
let mut p = PathBuf::from(r"foo");
p.truncate_to_root();
assert_eq!(p.as_path(), Path::new(r""));
}
}
mod any {
use super::*;
#[test]
fn test_pathinfo_is_absolute() {
let p = PathBuf::from("/foo/bar");
let expected = !cfg!(windows);
assert_eq!(<PathBuf as PathInfo>::is_absolute(&p), expected);
}
#[test]
fn test_pathinfo_parent() {
let p = PathBuf::from("/foo/bar");
let actual = <PathBuf as PathInfo>::parent(&p).expect("could not find parent?");
let expected = PathBuf::from("/foo");
assert_eq!(actual, expected);
let p = PathBuf::from("/");
let actual = <PathBuf as PathInfo>::parent(&p).expect_err("root has a parent?");
assert_eq!(actual.io_error().kind(), io::ErrorKind::NotFound);
assert_eq!(actual.action(), "truncating to parent");
assert_eq!(actual.path(), Path::new("/"));
}
#[test]
fn test_pathinfo_starts_with() {
let p = PathBuf::from("foo/bar");
assert_eq!(
<PathBuf as PathInfo>::starts_with(&p, Path::new("foo")),
true,
);
assert_eq!(
<PathBuf as PathInfo>::starts_with(&p, Path::new("bar")),
false,
);
}
#[test]
fn test_pathinfo_ends_with() {
let p = PathBuf::from("foo/bar");
assert_eq!(
<PathBuf as PathInfo>::ends_with(&p, Path::new("foo")),
false,
);
assert_eq!(<PathBuf as PathInfo>::ends_with(&p, Path::new("bar")), true,);
}
#[test]
fn test_pathops_concat() {
let actual = Path::new("foo")
.concat(Path::new("bar"))
.expect("Could not concat paths?");
let expected = PathBuf::from("foo/bar");
assert_eq!(actual, expected);
let actual = Path::new("foo")
.concat(Path::new("bar/../baz"))
.expect("Could not concat path with ..?");
let expected = PathBuf::from("foo/baz");
assert_eq!(actual, expected);
let actual = Path::new("foo")
.concat("..")
.expect("Could not cancel path with ..?");
let expected = PathBuf::from(r"");
assert_eq!(actual, expected);
let actual = Path::new("foo")
.concat("../..")
.expect("Could not prefix with ..?");
let expected = PathBuf::from(r"../");
assert_eq!(actual, expected);
let actual = Path::new("/foo")
.concat("../..")
.expect_err("Could escape root with ..?");
assert_eq!(actual.io_error().kind(), io::ErrorKind::NotFound);
assert_eq!(actual.action(), "truncating to parent");
assert_eq!(actual.path(), Path::new("/"));
let actual = Path::new("foo")
.concat(Path::new("/etc/passwd"))
.expect("Could not concat RootDir to path?");
let expected: PathBuf = PathBuf::from("foo/etc/passwd");
assert_eq!(actual, expected);
}
#[test]
fn test_pathops_concat_relative() {
let actual = Path::new("../foo")
.concat("bar")
.expect("Could not create relative path with concat");
let expected = PathBuf::from(r"../foo/bar");
assert_eq!(actual, expected);
let actual = Path::new("../foo")
.concat("..")
.expect("Could not create relative path with concat");
let expected = PathBuf::from(r"..");
assert_eq!(actual, expected);
let actual = Path::new("../foo")
.concat("../..")
.expect("Could not create relative path with concat");
let expected = PathBuf::from(r"../..");
assert_eq!(actual, expected);
let actual = Path::new("../foo/../bar")
.concat("../..")
.expect("Could not create relative path with concat");
let expected = PathBuf::from(r"../..");
assert_eq!(actual, expected);
let actual = Path::new("../foo/../bar/..")
.concat("../..")
.expect("Could not create relative path with concat");
let expected = PathBuf::from(r"../../..");
assert_eq!(actual, expected);
let actual = PathBuf::from("../foo/..")
.concat("../../baz")
.expect("Could not create relative path with concat");
let expected = PathBuf::from(r"../../../baz");
assert_eq!(actual, expected);
}
#[test]
fn test_pathops_concat_cur() {
let actual = Path::new("foo/././..").as_os_str();
let expected = ffi::OsStr::new("foo/././..");
assert_eq!(actual, expected);
let actual = PathBuf::from("././foo/././..")
.concat("../bar")
.expect("Could not create relative path with concat");
let expected = PathBuf::from(r"../bar");
assert_eq!(actual, expected);
}
#[test]
fn test_pathops_concat_consume() {
let actual = Path::new("foo")
.concat("../../bar")
.expect("Could not create relative path with concat");
let expected = PathBuf::from(r"../bar");
assert_eq!(actual, expected);
}
#[test]
fn test_pathmut_append() {
let mut actual = PathBuf::from("foo");
actual
.append(Path::new("bar"))
.expect("Could not append paths?");
let expected = PathBuf::from("foo/bar");
assert_eq!(actual, expected);
let mut actual = PathBuf::from("foo");
actual
.append(Path::new("bar/../baz"))
.expect("Could not append path with ..?");
let expected = PathBuf::from("foo/baz");
assert_eq!(actual, expected);
let mut actual = PathBuf::from("foo");
actual.append("..").expect("Could not cancel path with ..?");
let expected = PathBuf::from(r"");
assert_eq!(actual, expected);
let mut actual = PathBuf::from("foo");
actual
.append("../..")
.expect("Could not escape prefix with ..?");
let expected = PathBuf::from("../");
assert_eq!(actual, expected);
let actual = PathBuf::from("/foo")
.append("../..")
.expect_err("Could escape root with ..?");
assert_eq!(actual.io_error().kind(), io::ErrorKind::NotFound);
assert_eq!(actual.action(), "truncating to parent");
assert_eq!(actual.path(), Path::new("/"));
let mut actual = PathBuf::from("foo");
actual
.append(Path::new("/etc/passwd"))
.expect("Could not append RootDir to path?");
let expected: PathBuf = PathBuf::from("foo/etc/passwd");
assert_eq!(actual, expected);
}
#[test]
fn test_pathmut_pop_up() {
let mut p = PathBuf::from("/foo/bar");
p.pop_up().expect("could not find parent?");
assert_eq!(p.as_path(), Path::new("/foo"));
let mut p = PathBuf::from("/");
let actual = p.pop_up().expect_err("root has a parent?");
assert_eq!(actual.io_error().kind(), io::ErrorKind::NotFound);
assert_eq!(actual.action(), "truncating to parent");
assert_eq!(actual.path(), Path::new("/"));
}
#[test]
fn test_pathmut_truncate_to_root() {
let mut p = PathBuf::from("/foo/bar");
p.truncate_to_root();
assert_eq!(p.as_path(), Path::new("/"));
let mut p = PathBuf::from("foo/bar");
p.truncate_to_root();
assert_eq!(p.as_path(), Path::new(""));
}
}
}