path2: Rename pop_opt() to pop() and add each_parent()

This commit is contained in:
Kevin Ballard 2013-09-26 14:38:26 -07:00
parent 56b96a3bfc
commit 179f50f7c8
3 changed files with 152 additions and 18 deletions

View File

@ -540,13 +540,13 @@ pub trait GenericPath: Clone + GenericPathUnsafe {
}
/// Pops the last path component off of `self` and returns it.
/// If `self` represents the root of the file hierarchy, None is returned.
fn pop_opt(&mut self) -> Option<~[u8]>;
fn pop(&mut self) -> Option<~[u8]>;
/// Pops the last path component off of `self` and returns it as a string, if possible.
/// `self` will still be modified even if None is returned.
/// See `pop_opt` for details.
/// See `pop` for details.
#[inline]
fn pop_opt_str(&mut self) -> Option<~str> {
self.pop_opt().and_then(|v| str::from_utf8_owned_opt(v))
fn pop_str(&mut self) -> Option<~str> {
self.pop().and_then(|v| str::from_utf8_owned_opt(v))
}
/// Returns a new Path constructed by joining `self` with the given path (as a byte vector).
@ -593,6 +593,21 @@ pub trait GenericPath: Clone + GenericPathUnsafe {
/// If `self` is absolute and `base` is relative, or on Windows if both
/// paths refer to separate drives, an absolute path is returned.
fn path_relative_from(&self, base: &Self) -> Option<Self>;
/// Executes a callback with the receiver and every parent
fn each_parent(&self, f: &fn(&Self) -> bool) -> bool {
let mut p = self.clone();
loop {
if !f(&p) {
return false;
}
let f = p.pop();
if f.is_none() || bytes!("..") == f.unwrap() {
break;
}
}
true
}
}
/// A trait that represents the unsafe operations on GenericPaths

View File

@ -196,7 +196,7 @@ impl GenericPath for Path {
}
}
fn pop_opt(&mut self) -> Option<~[u8]> {
fn pop(&mut self) -> Option<~[u8]> {
match self.sepidx {
None if bytes!(".") == self.repr => None,
None => {
@ -400,7 +400,7 @@ fn normalize_helper<'a>(v: &'a [u8], is_abs: bool) -> Option<~[&'a [u8]]> {
else if comp == bytes!("..") {
if is_abs && comps.is_empty() { changed = true }
else if comps.len() == n_up { comps.push(dot_dot_static); n_up += 1 }
else { comps.pop_opt(); changed = true }
else { comps.pop(); changed = true }
} else { comps.push(comp) }
}
if changed {
@ -861,7 +861,7 @@ mod tests {
(s: $path:expr, $left:expr, $right:expr) => (
{
let mut p = Path::from_str($path);
let file = p.pop_opt_str();
let file = p.pop_str();
assert_eq!(p.as_str(), Some($left));
assert_eq!(file.map(|s| s.as_slice()), $right);
}
@ -869,7 +869,7 @@ mod tests {
(v: [$($path:expr),+], [$($left:expr),+], Some($($right:expr),+)) => (
{
let mut p = Path::from_vec(b!($($path),+));
let file = p.pop_opt();
let file = p.pop();
assert_eq!(p.as_vec(), b!($($left),+));
assert_eq!(file.map(|v| v.as_slice()), Some(b!($($right),+)));
}
@ -877,7 +877,7 @@ mod tests {
(v: [$($path:expr),+], [$($left:expr),+], None) => (
{
let mut p = Path::from_vec(b!($($path),+));
let file = p.pop_opt();
let file = p.pop();
assert_eq!(p.as_vec(), b!($($left),+));
assert_eq!(file, None);
}
@ -899,8 +899,8 @@ mod tests {
t!(s: "/a", "/", Some("a"));
t!(s: "/", "/", None);
assert_eq!(Path::from_vec(b!("foo/bar", 0x80)).pop_opt_str(), None);
assert_eq!(Path::from_vec(b!("foo", 0x80, "/bar")).pop_opt_str(), Some(~"bar"));
assert_eq!(Path::from_vec(b!("foo/bar", 0x80)).pop_str(), None);
assert_eq!(Path::from_vec(b!("foo", 0x80, "/bar")).pop_str(), Some(~"bar"));
}
#[test]
@ -1310,4 +1310,58 @@ mod tests {
t!(s: "../..", ["..", ".."]);
t!(s: "../../foo", ["..", "..", "foo"]);
}
#[test]
fn test_each_parent() {
assert!(Path::from_str("/foo/bar").each_parent(|_| true));
assert!(!Path::from_str("/foo/bar").each_parent(|_| false));
macro_rules! t(
(s: $path:expr, $exp:expr) => (
{
let path = Path::from_str($path);
let exp: &[&str] = $exp;
let mut comps = exp.iter().map(|&x|x);
do path.each_parent |p| {
let p = p.as_str();
assert!(p.is_some());
let e = comps.next();
assert!(e.is_some());
assert_eq!(p.unwrap(), e.unwrap());
true
};
assert!(comps.next().is_none());
}
);
(v: $path:expr, $exp:expr) => (
{
let path = Path::from_vec($path);
let exp: &[&[u8]] = $exp;
let mut comps = exp.iter().map(|&x|x);
do path.each_parent |p| {
let p = p.as_vec();
let e = comps.next();
assert!(e.is_some());
assert_eq!(p, e.unwrap());
true
};
assert!(comps.next().is_none());
}
)
)
t!(s: "/foo/bar", ["/foo/bar", "/foo", "/"]);
t!(s: "/foo/bar/baz", ["/foo/bar/baz", "/foo/bar", "/foo", "/"]);
t!(s: "/foo", ["/foo", "/"]);
t!(s: "/", ["/"]);
t!(s: "foo/bar/baz", ["foo/bar/baz", "foo/bar", "foo", "."]);
t!(s: "foo/bar", ["foo/bar", "foo", "."]);
t!(s: "foo", ["foo", "."]);
t!(s: ".", ["."]);
t!(s: "..", [".."]);
t!(s: "../../foo", ["../../foo", "../.."]);
t!(v: b!("foo/bar", 0x80), [b!("foo/bar", 0x80), b!("foo"), b!(".")]);
t!(v: b!(0xff, "/bar"), [b!(0xff, "/bar"), b!(0xff), b!(".")]);
}
}

View File

@ -446,11 +446,11 @@ impl GenericPath for Path {
}
#[inline]
fn pop_opt(&mut self) -> Option<~[u8]> {
self.pop_opt_str().map_move(|s| s.into_bytes())
fn pop(&mut self) -> Option<~[u8]> {
self.pop_str().map_move(|s| s.into_bytes())
}
fn pop_opt_str(&mut self) -> Option<~str> {
fn pop_str(&mut self) -> Option<~str> {
match self.sepidx_or_prefix_len() {
None if "." == self.repr => None,
None => {
@ -599,6 +599,21 @@ impl GenericPath for Path {
Some(Path::from_str(comps.connect("\\")))
}
}
/// Executes a callback with the receiver and every parent
fn each_parent(&self, f: &fn(&Path) -> bool) -> bool {
let mut p = self.clone();
loop {
if !f(&p) {
return false;
}
let f = p.pop();
if f.is_none() || (!p.is_verbatim() && bytes!("..") == f.unwrap()) {
break;
}
}
true
}
}
impl Path {
@ -1013,7 +1028,7 @@ fn normalize_helper<'a>(s: &'a str, prefix: Option<PathPrefix>) -> (bool,Option<
};
if (is_abs || has_abs_prefix) && comps.is_empty() { changed = true }
else if comps.len() == n_up { comps.push(".."); n_up += 1 }
else { comps.pop_opt(); changed = true }
else { comps.pop(); changed = true }
} else { comps.push(comp) }
}
if !changed && !prefix_is_verbatim(prefix) {
@ -1654,7 +1669,7 @@ mod tests {
{
let pstr = $path;
let mut p = Path::from_str(pstr);
let file = p.pop_opt_str();
let file = p.pop_str();
let left = $left;
assert!(p.as_str() == Some(left),
"`%s`.pop() failed; expected remainder `%s`, found `%s`",
@ -1668,7 +1683,7 @@ mod tests {
(v: [$($path:expr),+], [$($left:expr),+], Some($($right:expr),+)) => (
{
let mut p = Path::from_vec(b!($($path),+));
let file = p.pop_opt();
let file = p.pop();
assert_eq!(p.as_vec(), b!($($left),+));
assert_eq!(file.map(|v| v.as_slice()), Some(b!($($right),+)));
}
@ -1676,7 +1691,7 @@ mod tests {
(v: [$($path:expr),+], [$($left:expr),+], None) => (
{
let mut p = Path::from_vec(b!($($path),+));
let file = p.pop_opt();
let file = p.pop();
assert_eq!(p.as_vec(), b!($($left),+));
assert_eq!(file, None);
}
@ -2373,4 +2388,54 @@ mod tests {
t!(s: "\\\\.\\foo\\bar", ["bar"]);
t!(s: "\\\\.\\foo", []);
}
#[test]
fn test_each_parent() {
assert!(Path::from_str("/foo/bar").each_parent(|_| true));
assert!(!Path::from_str("/foo/bar").each_parent(|_| false));
macro_rules! t(
(s: $path:expr, $exp:expr) => (
{
let path = Path::from_str($path);
let exp: &[&str] = $exp;
let mut comps = exp.iter().map(|&x|x);
do path.each_parent |p| {
let p = p.as_str();
assert!(p.is_some());
let e = comps.next();
assert!(e.is_some());
assert_eq!(p.unwrap(), e.unwrap());
true
};
assert!(comps.next().is_none());
}
)
)
t!(s: "\\foo\\bar", ["\\foo\\bar", "\\foo", "\\"]);
t!(s: "\\foo\\bar\\baz", ["\\foo\\bar\\baz", "\\foo\\bar", "\\foo", "\\"]);
t!(s: "\\foo", ["\\foo", "\\"]);
t!(s: "\\", ["\\"]);
t!(s: "foo\\bar\\baz", ["foo\\bar\\baz", "foo\\bar", "foo", "."]);
t!(s: "foo\\bar", ["foo\\bar", "foo", "."]);
t!(s: "foo", ["foo", "."]);
t!(s: ".", ["."]);
t!(s: "..", [".."]);
t!(s: "..\\..\\foo", ["..\\..\\foo", "..\\.."]);
t!(s: "C:\\a\\b", ["C:\\a\\b", "C:\\a", "C:\\"]);
t!(s: "C:\\", ["C:\\"]);
t!(s: "C:a\\b", ["C:a\\b", "C:a", "C:"]);
t!(s: "C:", ["C:"]);
t!(s: "C:..\\..\\a", ["C:..\\..\\a", "C:..\\.."]);
t!(s: "C:..", ["C:.."]);
t!(s: "\\\\a\\b\\c", ["\\\\a\\b\\c", "\\\\a\\b"]);
t!(s: "\\\\a\\b", ["\\\\a\\b"]);
t!(s: "\\\\?\\a\\b\\c", ["\\\\?\\a\\b\\c", "\\\\?\\a\\b", "\\\\?\\a"]);
t!(s: "\\\\?\\C:\\a\\b", ["\\\\?\\C:\\a\\b", "\\\\?\\C:\\a", "\\\\?\\C:\\"]);
t!(s: "\\\\?\\UNC\\a\\b\\c", ["\\\\?\\UNC\\a\\b\\c", "\\\\?\\UNC\\a\\b"]);
t!(s: "\\\\.\\a\\b\\c", ["\\\\.\\a\\b\\c", "\\\\.\\a\\b", "\\\\.\\a"]);
t!(s: "\\\\?\\a\\..\\b\\.\\c/d", ["\\\\?\\a\\..\\b\\.\\c/d", "\\\\?\\a\\..\\b\\.",
"\\\\?\\a\\..\\b", "\\\\?\\a\\..", "\\\\?\\a"]);
}
}