path2: Update based on more review feedback
Standardize the is_sep() functions to be the same in both posix and windows, and re-export from path. Update extra::glob to use this. Remove the usage of either, as it's going away. Move the WindowsPath-specific methods out of WindowsPath and make them top-level functions of path::windows instead. This way you cannot accidentally write code that will fail to compile on non-windows architectures without typing ::windows anywhere. Remove GenericPath::from_c_str() and just impl BytesContainer for CString instead. Remove .join_path() and .push_path() and just implement BytesContainer for Path instead. Remove FilenameDisplay and add a boolean flag to Display instead. Remove .each_parent(). It only had one caller, so just inline its definition there.
This commit is contained in:
parent
c01a97b7a9
commit
bab7eb20df
17 changed files with 274 additions and 390 deletions
|
|
@ -78,7 +78,7 @@ pub fn getcwd() -> Path {
|
|||
fail2!()
|
||||
}
|
||||
|
||||
GenericPath::from_c_str(CString::new(buf as *c_char, false))
|
||||
Path::new(CString::new(buf as *c_char, false))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -608,7 +608,7 @@ pub fn tmpdir() -> Path {
|
|||
pub fn walk_dir(p: &Path, f: &fn(&Path) -> bool) -> bool {
|
||||
let r = list_dir(p);
|
||||
r.iter().advance(|q| {
|
||||
let path = &p.join_path(q);
|
||||
let path = &p.join(q);
|
||||
f(path) && (!path_is_dir(path) || walk_dir(path, |p| f(p)))
|
||||
})
|
||||
}
|
||||
|
|
@ -648,7 +648,7 @@ pub fn make_absolute(p: &Path) -> Path {
|
|||
p.clone()
|
||||
} else {
|
||||
let mut ret = getcwd();
|
||||
ret.push_path(p);
|
||||
ret.push(p);
|
||||
ret
|
||||
}
|
||||
}
|
||||
|
|
@ -730,7 +730,7 @@ pub fn list_dir(p: &Path) -> ~[Path] {
|
|||
let mut entry_ptr = readdir(dir_ptr);
|
||||
while (entry_ptr as uint != 0) {
|
||||
let cstr = CString::new(rust_list_dir_val(entry_ptr), false);
|
||||
paths.push(GenericPath::from_c_str(cstr));
|
||||
paths.push(Path::new(cstr));
|
||||
entry_ptr = readdir(dir_ptr);
|
||||
}
|
||||
closedir(dir_ptr);
|
||||
|
|
@ -800,7 +800,7 @@ pub fn list_dir(p: &Path) -> ~[Path] {
|
|||
* This version prepends each entry with the directory.
|
||||
*/
|
||||
pub fn list_dir_path(p: &Path) -> ~[Path] {
|
||||
list_dir(p).map(|f| p.join_path(f))
|
||||
list_dir(p).map(|f| p.join(f))
|
||||
}
|
||||
|
||||
/// Removes a directory at the specified path, after removing
|
||||
|
|
|
|||
|
|
@ -30,9 +30,7 @@ no restriction on paths beyond disallowing NUL).
|
|||
## Usage
|
||||
|
||||
Usage of this module is fairly straightforward. Unless writing platform-specific
|
||||
code, `Path` should be used to refer to the platform-native path, and methods
|
||||
used should be restricted to those defined in `GenericPath`, and those methods
|
||||
that are declared identically on both `PosixPath` and `WindowsPath`.
|
||||
code, `Path` should be used to refer to the platform-native path.
|
||||
|
||||
Creation of a path is typically done with either `Path::new(some_str)` or
|
||||
`Path::new(some_vec)`. This path can be modified with `.push()` and
|
||||
|
|
@ -69,7 +67,6 @@ debug2!("path exists: {}", b);
|
|||
use container::Container;
|
||||
use c_str::CString;
|
||||
use clone::Clone;
|
||||
use either::{Left, Right};
|
||||
use fmt;
|
||||
use iter::Iterator;
|
||||
use option::{Option, None, Some};
|
||||
|
|
@ -121,6 +118,19 @@ pub use StrComponentIter = self::windows::StrComponentIter;
|
|||
#[cfg(windows)]
|
||||
pub use RevStrComponentIter = self::windows::RevStrComponentIter;
|
||||
|
||||
/// Typedef for the platform-native separator char func
|
||||
#[cfg(unix)]
|
||||
pub use is_sep = self::posix::is_sep;
|
||||
/// Typedef for the platform-native separator char func
|
||||
#[cfg(windows)]
|
||||
pub use is_sep = self::windows::is_sep;
|
||||
/// Typedef for the platform-native separator byte func
|
||||
#[cfg(unix)]
|
||||
pub use is_sep_byte = self::posix::is_sep_byte;
|
||||
/// Typedef for the platform-native separator byte func
|
||||
#[cfg(windows)]
|
||||
pub use is_sep_byte = self::windows::is_sep_byte;
|
||||
|
||||
pub mod posix;
|
||||
pub mod windows;
|
||||
|
||||
|
|
@ -162,19 +172,6 @@ pub trait GenericPath: Clone + GenericPathUnsafe {
|
|||
}
|
||||
}
|
||||
|
||||
/// Creates a new Path from a CString.
|
||||
/// The resulting Path will always be normalized.
|
||||
///
|
||||
/// See individual Path impls for potential restrictions.
|
||||
#[inline]
|
||||
fn from_c_str(path: CString) -> Self {
|
||||
// CStrings can't contain NULs
|
||||
let v = path.as_bytes();
|
||||
// v is NUL-terminated. Strip it off
|
||||
let v = v.slice_to(v.len()-1);
|
||||
unsafe { GenericPathUnsafe::new_unchecked(v) }
|
||||
}
|
||||
|
||||
/// Returns the path as a string, if possible.
|
||||
/// If the path is not representable in utf-8, this returns None.
|
||||
#[inline]
|
||||
|
|
@ -195,15 +192,15 @@ pub trait GenericPath: Clone + GenericPathUnsafe {
|
|||
///
|
||||
/// This will print the equivalent of `to_display_str()` when used with a {} format parameter.
|
||||
fn display<'a>(&'a self) -> Display<'a, Self> {
|
||||
Display{ path: self }
|
||||
Display{ path: self, filename: false }
|
||||
}
|
||||
|
||||
/// Returns an object that implements `fmt::Default` for printing filenames
|
||||
///
|
||||
/// This will print the equivalent of `to_filename_display_str()` when used with a {}
|
||||
/// format parameter. If there is no filename, nothing will be printed.
|
||||
fn filename_display<'a>(&'a self) -> FilenameDisplay<'a, Self> {
|
||||
FilenameDisplay{ path: self }
|
||||
fn filename_display<'a>(&'a self) -> Display<'a, Self> {
|
||||
Display{ path: self, filename: true }
|
||||
}
|
||||
|
||||
/// Returns the directory component of `self`, as a byte vector (with no trailing separator).
|
||||
|
|
@ -314,13 +311,17 @@ pub trait GenericPath: Clone + GenericPathUnsafe {
|
|||
/// Raises the `null_byte` condition if the filestem contains a NUL.
|
||||
fn set_filestem<T: BytesContainer>(&mut self, filestem: T) {
|
||||
// borrowck is being a pain here
|
||||
enum Value<T> {
|
||||
Checked(T),
|
||||
Unchecked(~[u8])
|
||||
}
|
||||
let val = {
|
||||
match self.filename() {
|
||||
None => Left(filestem),
|
||||
None => Checked(filestem),
|
||||
Some(name) => {
|
||||
let dot = '.' as u8;
|
||||
match name.rposition_elem(&dot) {
|
||||
None | Some(0) => Left(filestem),
|
||||
None | Some(0) => Checked(filestem),
|
||||
Some(idx) => {
|
||||
let mut v;
|
||||
if contains_nul(filestem.container_as_bytes()) {
|
||||
|
|
@ -336,15 +337,15 @@ pub trait GenericPath: Clone + GenericPathUnsafe {
|
|||
v.push_all(filestem);
|
||||
}
|
||||
v.push_all(name.slice_from(idx));
|
||||
Right(v)
|
||||
Unchecked(v)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
match val {
|
||||
Left(v) => self.set_filename(v),
|
||||
Right(v) => unsafe { self.set_filename_unchecked(v) }
|
||||
Checked(v) => self.set_filename(v),
|
||||
Unchecked(v) => unsafe { self.set_filename_unchecked(v) }
|
||||
}
|
||||
}
|
||||
/// Replaces the extension with the given byte vector or string.
|
||||
|
|
@ -545,12 +546,6 @@ pub trait GenericPath: Clone + GenericPathUnsafe {
|
|||
unsafe { self.push_unchecked(path) }
|
||||
}
|
||||
}
|
||||
/// Pushes a Path onto `self`.
|
||||
/// If the argument represents an absolute path, it replaces `self`.
|
||||
#[inline]
|
||||
fn push_path(&mut self, path: &Self) {
|
||||
self.push(path.as_vec())
|
||||
}
|
||||
/// Pushes multiple paths (as byte vectors or strings) onto `self`.
|
||||
/// See `push` for details.
|
||||
#[inline]
|
||||
|
|
@ -590,14 +585,6 @@ pub trait GenericPath: Clone + GenericPathUnsafe {
|
|||
p.push(path);
|
||||
p
|
||||
}
|
||||
/// Returns a new Path constructed by joining `self` with the given path.
|
||||
/// If the given path is absolute, the new Path will represent just that.
|
||||
#[inline]
|
||||
fn join_path(&self, path: &Self) -> Self {
|
||||
let mut p = self.clone();
|
||||
p.push_path(path);
|
||||
p
|
||||
}
|
||||
/// Returns a new Path constructed by joining `self` with the given paths
|
||||
/// (as byte vectors or strings).
|
||||
/// See `join` for details.
|
||||
|
|
@ -632,21 +619,6 @@ pub trait GenericPath: Clone + GenericPathUnsafe {
|
|||
/// 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
|
||||
}
|
||||
|
||||
/// Returns whether the relative path `child` is a suffix of `self`.
|
||||
fn ends_with_path(&self, child: &Self) -> bool;
|
||||
}
|
||||
|
|
@ -674,7 +646,7 @@ pub trait BytesContainer {
|
|||
fn container_as_str_opt<'a>(&'a self) -> Option<&'a str> {
|
||||
str::from_utf8_slice_opt(self.container_as_bytes())
|
||||
}
|
||||
/// Returns whether the concrete receiver is a string type
|
||||
/// Returns whether .container_as_str() is guaranteed to not fail
|
||||
// FIXME (#8888): Remove unused arg once ::<for T> works
|
||||
#[inline]
|
||||
fn is_str(_: Option<Self>) -> bool { false }
|
||||
|
|
@ -703,11 +675,8 @@ pub trait GenericPathUnsafe {
|
|||
|
||||
/// Helper struct for printing paths with format!()
|
||||
pub struct Display<'self, P> {
|
||||
priv path: &'self P
|
||||
}
|
||||
/// Helper struct for printing filenames with format!()
|
||||
pub struct FilenameDisplay<'self, P> {
|
||||
priv path: &'self P
|
||||
priv path: &'self P,
|
||||
priv filename: bool
|
||||
}
|
||||
|
||||
impl<'self, P: GenericPath> fmt::Default for Display<'self, P> {
|
||||
|
|
@ -724,7 +693,14 @@ impl<'self, P: GenericPath> ToStr for Display<'self, P> {
|
|||
/// If the path is not UTF-8, invalid sequences with be replaced with the
|
||||
/// unicode replacement char. This involves allocation.
|
||||
fn to_str(&self) -> ~str {
|
||||
from_utf8_with_replacement(self.path.as_vec())
|
||||
if self.filename {
|
||||
match self.path.filename() {
|
||||
None => ~"",
|
||||
Some(v) => from_utf8_with_replacement(v)
|
||||
}
|
||||
} else {
|
||||
from_utf8_with_replacement(self.path.as_vec())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -735,47 +711,9 @@ impl<'self, P: GenericPath> Display<'self, P> {
|
|||
/// unicode replacement char. This involves allocation.
|
||||
#[inline]
|
||||
pub fn with_str<T>(&self, f: &fn(&str) -> T) -> T {
|
||||
match self.path.as_str() {
|
||||
Some(s) => f(s),
|
||||
None => {
|
||||
let s = self.to_str();
|
||||
f(s.as_slice())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'self, P: GenericPath> fmt::Default for FilenameDisplay<'self, P> {
|
||||
fn fmt(d: &FilenameDisplay<P>, f: &mut fmt::Formatter) {
|
||||
do d.with_str |s| {
|
||||
f.pad(s)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'self, P: GenericPath> ToStr for FilenameDisplay<'self, P> {
|
||||
/// Returns the filename as a string. If there is no filename, ~"" will be
|
||||
/// returned.
|
||||
///
|
||||
/// If the filename is not UTF-8, invalid sequences will be replaced with
|
||||
/// the unicode replacement char. This involves allocation.
|
||||
fn to_str(&self) -> ~str {
|
||||
match self.path.filename() {
|
||||
None => ~"",
|
||||
Some(v) => from_utf8_with_replacement(v)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'self, P: GenericPath> FilenameDisplay<'self, P> {
|
||||
/// Provides the filename as a string to a closure. If there is no
|
||||
/// filename, "" will be provided.
|
||||
///
|
||||
/// If the filename is not UTF-8, invalid sequences will be replaced with
|
||||
/// the unicode replacement char. This involves allocation.
|
||||
#[inline]
|
||||
pub fn with_str<T>(&self, f: &fn(&str) -> T) -> T {
|
||||
match self.path.filename_str() {
|
||||
let opt = if self.filename { self.path.filename_str() }
|
||||
else { self.path.as_str() };
|
||||
match opt {
|
||||
Some(s) => f(s),
|
||||
None => {
|
||||
let s = self.to_str();
|
||||
|
|
@ -865,6 +803,14 @@ impl BytesContainer for @[u8] {
|
|||
}
|
||||
}
|
||||
|
||||
impl BytesContainer for CString {
|
||||
#[inline]
|
||||
fn container_as_bytes<'a>(&'a self) -> &'a [u8] {
|
||||
let s = self.as_bytes();
|
||||
s.slice_to(s.len()-1)
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn contains_nul(v: &[u8]) -> bool {
|
||||
v.iter().any(|&x| x == 0)
|
||||
|
|
@ -1121,13 +1067,13 @@ mod tests {
|
|||
use c_str::ToCStr;
|
||||
|
||||
#[test]
|
||||
fn test_from_c_str() {
|
||||
fn test_cstring() {
|
||||
let input = "/foo/bar/baz";
|
||||
let path: PosixPath = GenericPath::from_c_str(input.to_c_str());
|
||||
let path: PosixPath = PosixPath::new(input.to_c_str());
|
||||
assert_eq!(path.as_vec(), input.as_bytes());
|
||||
|
||||
let input = "\\foo\\bar\\baz";
|
||||
let path: WindowsPath = GenericPath::from_c_str(input.to_c_str());
|
||||
let path: WindowsPath = WindowsPath::new(input.to_c_str());
|
||||
assert_eq!(path.as_str().unwrap(), input.as_slice());
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -48,12 +48,19 @@ pub struct Path {
|
|||
}
|
||||
|
||||
/// The standard path separator character
|
||||
pub static sep: u8 = '/' as u8;
|
||||
pub static sep: char = '/';
|
||||
static sep_byte: u8 = sep as u8;
|
||||
|
||||
/// Returns whether the given byte is a path separator
|
||||
#[inline]
|
||||
pub fn is_sep(u: &u8) -> bool {
|
||||
*u == sep
|
||||
pub fn is_sep_byte(u: &u8) -> bool {
|
||||
*u as char == sep
|
||||
}
|
||||
|
||||
/// Returns whether the given char is a path separator
|
||||
#[inline]
|
||||
pub fn is_sep(c: char) -> bool {
|
||||
c == sep
|
||||
}
|
||||
|
||||
impl Eq for Path {
|
||||
|
|
@ -89,11 +96,29 @@ impl IterBytes for Path {
|
|||
}
|
||||
}
|
||||
|
||||
impl BytesContainer for Path {
|
||||
#[inline]
|
||||
fn container_as_bytes<'a>(&'a self) -> &'a [u8] {
|
||||
self.as_vec()
|
||||
}
|
||||
#[inline]
|
||||
fn container_into_owned_bytes(self) -> ~[u8] {
|
||||
self.into_vec()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'self> BytesContainer for &'self Path {
|
||||
#[inline]
|
||||
fn container_as_bytes<'a>(&'a self) -> &'a [u8] {
|
||||
self.as_vec()
|
||||
}
|
||||
}
|
||||
|
||||
impl GenericPathUnsafe for Path {
|
||||
unsafe fn new_unchecked<T: BytesContainer>(path: T) -> Path {
|
||||
let path = Path::normalize(path.container_as_bytes());
|
||||
assert!(!path.is_empty());
|
||||
let idx = path.rposition_elem(&sep);
|
||||
let idx = path.rposition_elem(&sep_byte);
|
||||
Path{ repr: path, sepidx: idx }
|
||||
}
|
||||
|
||||
|
|
@ -106,11 +131,11 @@ impl GenericPathUnsafe for Path {
|
|||
None => {
|
||||
let mut v = vec::with_capacity(dirname.len() + self.repr.len() + 1);
|
||||
v.push_all(dirname);
|
||||
v.push(sep);
|
||||
v.push(sep_byte);
|
||||
v.push_all(self.repr);
|
||||
self.repr = Path::normalize(v);
|
||||
}
|
||||
Some(0) if self.repr.len() == 1 && self.repr[0] == sep => {
|
||||
Some(0) if self.repr.len() == 1 && self.repr[0] == sep_byte => {
|
||||
self.repr = Path::normalize(dirname);
|
||||
}
|
||||
Some(idx) if self.repr.slice_from(idx+1) == bytes!("..") => {
|
||||
|
|
@ -127,7 +152,7 @@ impl GenericPathUnsafe for Path {
|
|||
self.repr = Path::normalize(v);
|
||||
}
|
||||
}
|
||||
self.sepidx = self.repr.rposition_elem(&sep);
|
||||
self.sepidx = self.repr.rposition_elem(&sep_byte);
|
||||
}
|
||||
|
||||
unsafe fn set_filename_unchecked<T: BytesContainer>(&mut self, filename: T) {
|
||||
|
|
@ -136,7 +161,7 @@ impl GenericPathUnsafe for Path {
|
|||
None if bytes!("..") == self.repr => {
|
||||
let mut v = vec::with_capacity(3 + filename.len());
|
||||
v.push_all(dot_dot_static);
|
||||
v.push(sep);
|
||||
v.push(sep_byte);
|
||||
v.push_all(filename);
|
||||
self.repr = Path::normalize(v);
|
||||
}
|
||||
|
|
@ -146,7 +171,7 @@ impl GenericPathUnsafe for Path {
|
|||
Some(idx) if self.repr.slice_from(idx+1) == bytes!("..") => {
|
||||
let mut v = vec::with_capacity(self.repr.len() + 1 + filename.len());
|
||||
v.push_all(self.repr);
|
||||
v.push(sep);
|
||||
v.push(sep_byte);
|
||||
v.push_all(filename);
|
||||
self.repr = Path::normalize(v);
|
||||
}
|
||||
|
|
@ -157,22 +182,22 @@ impl GenericPathUnsafe for Path {
|
|||
self.repr = Path::normalize(v);
|
||||
}
|
||||
}
|
||||
self.sepidx = self.repr.rposition_elem(&sep);
|
||||
self.sepidx = self.repr.rposition_elem(&sep_byte);
|
||||
}
|
||||
|
||||
unsafe fn push_unchecked<T: BytesContainer>(&mut self, path: T) {
|
||||
let path = path.container_as_bytes();
|
||||
if !path.is_empty() {
|
||||
if path[0] == sep {
|
||||
if path[0] == sep_byte {
|
||||
self.repr = Path::normalize(path);
|
||||
} else {
|
||||
let mut v = vec::with_capacity(self.repr.len() + path.len() + 1);
|
||||
v.push_all(self.repr);
|
||||
v.push(sep);
|
||||
v.push(sep_byte);
|
||||
v.push_all(path);
|
||||
self.repr = Path::normalize(v);
|
||||
}
|
||||
self.sepidx = self.repr.rposition_elem(&sep);
|
||||
self.sepidx = self.repr.rposition_elem(&sep_byte);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -228,7 +253,7 @@ impl GenericPath for Path {
|
|||
} else {
|
||||
self.repr.truncate(idx);
|
||||
}
|
||||
self.sepidx = self.repr.rposition_elem(&sep);
|
||||
self.sepidx = self.repr.rposition_elem(&sep_byte);
|
||||
Some(v)
|
||||
}
|
||||
}
|
||||
|
|
@ -244,7 +269,7 @@ impl GenericPath for Path {
|
|||
|
||||
#[inline]
|
||||
fn is_absolute(&self) -> bool {
|
||||
self.repr[0] == sep
|
||||
self.repr[0] == sep_byte
|
||||
}
|
||||
|
||||
fn is_ancestor_of(&self, other: &Path) -> bool {
|
||||
|
|
@ -305,7 +330,7 @@ impl GenericPath for Path {
|
|||
}
|
||||
}
|
||||
}
|
||||
Some(Path::new(comps.connect_vec(&sep)))
|
||||
Some(Path::new(comps.connect_vec(&sep_byte)))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -347,14 +372,14 @@ impl Path {
|
|||
fn normalize<V: Vector<u8>+CopyableVector<u8>>(v: V) -> ~[u8] {
|
||||
// borrowck is being very picky
|
||||
let val = {
|
||||
let is_abs = !v.as_slice().is_empty() && v.as_slice()[0] == sep;
|
||||
let is_abs = !v.as_slice().is_empty() && v.as_slice()[0] == sep_byte;
|
||||
let v_ = if is_abs { v.as_slice().slice_from(1) } else { v.as_slice() };
|
||||
let comps = normalize_helper(v_, is_abs);
|
||||
match comps {
|
||||
None => None,
|
||||
Some(comps) => {
|
||||
if is_abs && comps.is_empty() {
|
||||
Some(~[sep])
|
||||
Some(~[sep_byte])
|
||||
} else {
|
||||
let n = if is_abs { comps.len() } else { comps.len() - 1} +
|
||||
comps.iter().map(|v| v.len()).sum();
|
||||
|
|
@ -367,7 +392,7 @@ impl Path {
|
|||
}
|
||||
}
|
||||
for comp in it {
|
||||
v.push(sep);
|
||||
v.push(sep_byte);
|
||||
v.push_all(comp);
|
||||
}
|
||||
Some(v)
|
||||
|
|
@ -386,10 +411,10 @@ impl Path {
|
|||
/// /a/b/c and a/b/c yield the same set of components.
|
||||
/// A path of "/" yields no components. A path of "." yields one component.
|
||||
pub fn component_iter<'a>(&'a self) -> ComponentIter<'a> {
|
||||
let v = if self.repr[0] == sep {
|
||||
let v = if self.repr[0] == sep_byte {
|
||||
self.repr.slice_from(1)
|
||||
} else { self.repr.as_slice() };
|
||||
let mut ret = v.split_iter(is_sep);
|
||||
let mut ret = v.split_iter(is_sep_byte);
|
||||
if v.is_empty() {
|
||||
// consume the empty "" component
|
||||
ret.next();
|
||||
|
|
@ -400,10 +425,10 @@ impl Path {
|
|||
/// Returns an iterator that yields each component of the path in reverse.
|
||||
/// See component_iter() for details.
|
||||
pub fn rev_component_iter<'a>(&'a self) -> RevComponentIter<'a> {
|
||||
let v = if self.repr[0] == sep {
|
||||
let v = if self.repr[0] == sep_byte {
|
||||
self.repr.slice_from(1)
|
||||
} else { self.repr.as_slice() };
|
||||
let mut ret = v.rsplit_iter(is_sep);
|
||||
let mut ret = v.rsplit_iter(is_sep_byte);
|
||||
if v.is_empty() {
|
||||
// consume the empty "" component
|
||||
ret.next();
|
||||
|
|
@ -432,7 +457,7 @@ fn normalize_helper<'a>(v: &'a [u8], is_abs: bool) -> Option<~[&'a [u8]]> {
|
|||
let mut comps: ~[&'a [u8]] = ~[];
|
||||
let mut n_up = 0u;
|
||||
let mut changed = false;
|
||||
for comp in v.split_iter(is_sep) {
|
||||
for comp in v.split_iter(is_sep_byte) {
|
||||
if comp.is_empty() { changed = true }
|
||||
else if comp == bytes!(".") { changed = true }
|
||||
else if comp == bytes!("..") {
|
||||
|
|
@ -928,7 +953,7 @@ mod tests {
|
|||
{
|
||||
let mut p = Path::new($path);
|
||||
let push = Path::new($push);
|
||||
p.push_path(&push);
|
||||
p.push(&push);
|
||||
assert_eq!(p.as_str(), Some($exp));
|
||||
}
|
||||
)
|
||||
|
|
@ -1049,7 +1074,7 @@ mod tests {
|
|||
{
|
||||
let path = Path::new($path);
|
||||
let join = Path::new($join);
|
||||
let res = path.join_path(&join);
|
||||
let res = path.join(&join);
|
||||
assert_eq!(res.as_str(), Some($exp));
|
||||
}
|
||||
)
|
||||
|
|
@ -1598,58 +1623,4 @@ mod tests {
|
|||
// str_component_iter is a wrapper around component_iter, so no need to do
|
||||
// the full set of tests
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_each_parent() {
|
||||
assert!(Path::new("/foo/bar").each_parent(|_| true));
|
||||
assert!(!Path::new("/foo/bar").each_parent(|_| false));
|
||||
|
||||
macro_rules! t(
|
||||
(s: $path:expr, $exp:expr) => (
|
||||
{
|
||||
let path = Path::new($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::new($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!(".")]);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -121,6 +121,44 @@ impl IterBytes for Path {
|
|||
}
|
||||
}
|
||||
|
||||
impl BytesContainer for Path {
|
||||
#[inline]
|
||||
fn container_as_bytes<'a>(&'a self) -> &'a [u8] {
|
||||
self.as_vec()
|
||||
}
|
||||
#[inline]
|
||||
fn container_into_owned_bytes(self) -> ~[u8] {
|
||||
self.into_vec()
|
||||
}
|
||||
#[inline]
|
||||
fn container_as_str<'a>(&'a self) -> &'a str {
|
||||
self.as_str().unwrap()
|
||||
}
|
||||
#[inline]
|
||||
fn container_as_str_opt<'a>(&'a self) -> Option<&'a str> {
|
||||
self.as_str()
|
||||
}
|
||||
#[inline]
|
||||
fn is_str(_: Option<Path>) -> bool { true }
|
||||
}
|
||||
|
||||
impl<'self> BytesContainer for &'self Path {
|
||||
#[inline]
|
||||
fn container_as_bytes<'a>(&'a self) -> &'a [u8] {
|
||||
self.as_vec()
|
||||
}
|
||||
#[inline]
|
||||
fn container_as_str<'a>(&'a self) -> &'a str {
|
||||
self.as_str().unwrap()
|
||||
}
|
||||
#[inline]
|
||||
fn container_as_str_opt<'a>(&'a self) -> Option<&'a str> {
|
||||
self.as_str()
|
||||
}
|
||||
#[inline]
|
||||
fn is_str(_: Option<&'self Path>) -> bool { true }
|
||||
}
|
||||
|
||||
impl GenericPathUnsafe for Path {
|
||||
/// See `GenericPathUnsafe::from_vec_unchecked`.
|
||||
///
|
||||
|
|
@ -235,7 +273,7 @@ impl GenericPathUnsafe for Path {
|
|||
fn is_vol_abs(path: &str, prefix: Option<PathPrefix>) -> bool {
|
||||
// assume prefix is Some(DiskPrefix)
|
||||
let rest = path.slice_from(prefix_len(prefix));
|
||||
!rest.is_empty() && rest[0].is_ascii() && is_sep2(rest[0] as char)
|
||||
!rest.is_empty() && rest[0].is_ascii() && is_sep(rest[0] as char)
|
||||
}
|
||||
fn shares_volume(me: &Path, path: &str) -> bool {
|
||||
// path is assumed to have a prefix of Some(DiskPrefix)
|
||||
|
|
@ -246,8 +284,8 @@ impl GenericPathUnsafe for Path {
|
|||
}
|
||||
}
|
||||
fn is_sep_(prefix: Option<PathPrefix>, u: u8) -> bool {
|
||||
u.is_ascii() && if prefix_is_verbatim(prefix) { is_sep(u as char) }
|
||||
else { is_sep2(u as char) }
|
||||
if prefix_is_verbatim(prefix) { is_sep_verbatim(u as char) }
|
||||
else { is_sep(u as char) }
|
||||
}
|
||||
|
||||
fn replace_path(me: &mut Path, path: &str, prefix: Option<PathPrefix>) {
|
||||
|
|
@ -262,7 +300,7 @@ impl GenericPathUnsafe for Path {
|
|||
fn append_path(me: &mut Path, path: &str) {
|
||||
// appends a path that has no prefix
|
||||
// if me is verbatim, we need to pre-normalize the new path
|
||||
let path_ = if me.is_verbatim() { Path::normalize__(path, None) }
|
||||
let path_ = if is_verbatim(me) { Path::normalize__(path, None) }
|
||||
else { None };
|
||||
let pathlen = path_.map_default(path.len(), |p| p.len());
|
||||
let mut s = str::with_capacity(me.repr.len() + 1 + pathlen);
|
||||
|
|
@ -291,7 +329,7 @@ impl GenericPathUnsafe for Path {
|
|||
}
|
||||
None if !path.is_empty() && is_sep_(self.prefix, path[0]) => {
|
||||
// volume-relative path
|
||||
if self.prefix().is_some() {
|
||||
if self.prefix.is_some() {
|
||||
// truncate self down to the prefix, then append
|
||||
let n = self.prefix_len();
|
||||
self.repr.truncate(n);
|
||||
|
|
@ -418,11 +456,6 @@ impl GenericPath for Path {
|
|||
self.filename_str().map_move(|s| unsafe { GenericPathUnsafe::new_unchecked(s) })
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn push_path(&mut self, path: &Path) {
|
||||
self.push(path.as_str().unwrap())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn pop(&mut self) -> Option<~[u8]> {
|
||||
self.pop_str().map_move(|s| s.into_bytes())
|
||||
|
|
@ -463,7 +496,7 @@ impl GenericPath for Path {
|
|||
}
|
||||
_ => self.repr.slice_to(self.prefix_len())
|
||||
}))
|
||||
} else if self.is_vol_relative() {
|
||||
} else if is_vol_relative(self) {
|
||||
Some(Path::new(self.repr.slice_to(1)))
|
||||
} else {
|
||||
None
|
||||
|
|
@ -493,14 +526,14 @@ impl GenericPath for Path {
|
|||
|
||||
#[inline]
|
||||
fn is_relative(&self) -> bool {
|
||||
self.prefix.is_none() && !self.is_vol_relative()
|
||||
self.prefix.is_none() && !is_vol_relative(self)
|
||||
}
|
||||
|
||||
fn is_ancestor_of(&self, other: &Path) -> bool {
|
||||
if !self.equiv_prefix(other) {
|
||||
false
|
||||
} else if self.is_absolute() != other.is_absolute() ||
|
||||
self.is_vol_relative() != other.is_vol_relative() {
|
||||
is_vol_relative(self) != is_vol_relative(other) {
|
||||
false
|
||||
} else {
|
||||
let mut ita = self.str_component_iter().map(|x|x.unwrap());
|
||||
|
|
@ -544,8 +577,8 @@ impl GenericPath for Path {
|
|||
} else {
|
||||
None
|
||||
}
|
||||
} else if self.is_vol_relative() != base.is_vol_relative() {
|
||||
if self.is_vol_relative() {
|
||||
} else if is_vol_relative(self) != is_vol_relative(base) {
|
||||
if is_vol_relative(self) {
|
||||
Some(self.clone())
|
||||
} else {
|
||||
None
|
||||
|
|
@ -555,8 +588,8 @@ impl GenericPath for Path {
|
|||
let mut itb = base.str_component_iter().map(|x|x.unwrap());
|
||||
let mut comps = ~[];
|
||||
|
||||
let a_verb = self.is_verbatim();
|
||||
let b_verb = base.is_verbatim();
|
||||
let a_verb = is_verbatim(self);
|
||||
let b_verb = is_verbatim(base);
|
||||
loop {
|
||||
match (ita.next(), itb.next()) {
|
||||
(None, None) => break,
|
||||
|
|
@ -598,20 +631,6 @@ impl GenericPath for Path {
|
|||
}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
fn ends_with_path(&self, child: &Path) -> bool {
|
||||
if !child.is_relative() { return false; }
|
||||
let mut selfit = self.str_component_iter().invert();
|
||||
|
|
@ -694,34 +713,6 @@ impl Path {
|
|||
self.rev_str_component_iter().map(convert)
|
||||
}
|
||||
|
||||
/// Returns whether the path is considered "volume-relative", which means a path
|
||||
/// that looks like "\foo". Paths of this form are relative to the current volume,
|
||||
/// but absolute within that volume.
|
||||
#[inline]
|
||||
pub fn is_vol_relative(&self) -> bool {
|
||||
self.prefix.is_none() && self.repr[0] == sep as u8
|
||||
}
|
||||
|
||||
/// Returns whether the path is considered "cwd-relative", which means a path
|
||||
/// with a volume prefix that is not absolute. This look like "C:foo.txt". Paths
|
||||
/// of this form are relative to the cwd on the given volume.
|
||||
#[inline]
|
||||
pub fn is_cwd_relative(&self) -> bool {
|
||||
self.prefix == Some(DiskPrefix) && !self.is_absolute()
|
||||
}
|
||||
|
||||
/// Returns the PathPrefix for this Path
|
||||
#[inline]
|
||||
pub fn prefix(&self) -> Option<PathPrefix> {
|
||||
self.prefix
|
||||
}
|
||||
|
||||
/// Returns whether the prefix is a verbatim prefix, i.e. \\?\
|
||||
#[inline]
|
||||
pub fn is_verbatim(&self) -> bool {
|
||||
prefix_is_verbatim(self.prefix)
|
||||
}
|
||||
|
||||
fn equiv_prefix(&self, other: &Path) -> bool {
|
||||
match (self.prefix, other.prefix) {
|
||||
(Some(DiskPrefix), Some(VerbatimDiskPrefix)) => {
|
||||
|
|
@ -866,8 +857,8 @@ impl Path {
|
|||
let s = if self.has_nonsemantic_trailing_slash() {
|
||||
self.repr.slice_to(self.repr.len()-1)
|
||||
} else { self.repr.as_slice() };
|
||||
let idx = s.rfind(if !prefix_is_verbatim(self.prefix) { is_sep2 }
|
||||
else { is_sep });
|
||||
let idx = s.rfind(if !prefix_is_verbatim(self.prefix) { is_sep }
|
||||
else { is_sep_verbatim });
|
||||
let prefixlen = self.prefix_len();
|
||||
self.sepidx = idx.and_then(|x| if x < prefixlen { None } else { Some(x) });
|
||||
}
|
||||
|
|
@ -893,7 +884,7 @@ impl Path {
|
|||
}
|
||||
|
||||
fn has_nonsemantic_trailing_slash(&self) -> bool {
|
||||
self.is_verbatim() && self.repr.len() > self.prefix_len()+1 &&
|
||||
is_verbatim(self) && self.repr.len() > self.prefix_len()+1 &&
|
||||
self.repr[self.repr.len()-1] == sep as u8
|
||||
}
|
||||
|
||||
|
|
@ -905,23 +896,65 @@ impl Path {
|
|||
}
|
||||
}
|
||||
|
||||
/// Returns whether the path is considered "volume-relative", which means a path
|
||||
/// that looks like "\foo". Paths of this form are relative to the current volume,
|
||||
/// but absolute within that volume.
|
||||
#[inline]
|
||||
pub fn is_vol_relative(path: &Path) -> bool {
|
||||
path.prefix.is_none() && is_sep_byte(&path.repr[0])
|
||||
}
|
||||
|
||||
/// Returns whether the path is considered "cwd-relative", which means a path
|
||||
/// with a volume prefix that is not absolute. This look like "C:foo.txt". Paths
|
||||
/// of this form are relative to the cwd on the given volume.
|
||||
#[inline]
|
||||
pub fn is_cwd_relative(path: &Path) -> bool {
|
||||
path.prefix == Some(DiskPrefix) && !path.is_absolute()
|
||||
}
|
||||
|
||||
/// Returns the PathPrefix for this Path
|
||||
#[inline]
|
||||
pub fn prefix(path: &Path) -> Option<PathPrefix> {
|
||||
path.prefix
|
||||
}
|
||||
|
||||
/// Returns whether the Path's prefix is a verbatim prefix, i.e. \\?\
|
||||
#[inline]
|
||||
pub fn is_verbatim(path: &Path) -> bool {
|
||||
prefix_is_verbatim(path.prefix)
|
||||
}
|
||||
|
||||
/// The standard path separator character
|
||||
pub static sep: char = '\\';
|
||||
/// The alternative path separator character
|
||||
pub static sep2: char = '/';
|
||||
|
||||
/// Returns whether the given byte is a path separator.
|
||||
/// Only allows the primary separator '\'; use is_sep2 to allow '/'.
|
||||
/// Returns whether the given char is a path separator.
|
||||
/// Allows both the primary separator '\' and the alternative separator '/'.
|
||||
#[inline]
|
||||
pub fn is_sep(c: char) -> bool {
|
||||
c == sep || c == sep2
|
||||
}
|
||||
|
||||
/// Returns whether the given char is a path separator.
|
||||
/// Only allows the primary separator '\'; use is_sep to allow '/'.
|
||||
#[inline]
|
||||
pub fn is_sep_verbatim(c: char) -> bool {
|
||||
c == sep
|
||||
}
|
||||
|
||||
/// Returns whether the given byte is a path separator.
|
||||
/// Allows both the primary separator '\' and the alternative separator '/'.
|
||||
#[inline]
|
||||
pub fn is_sep2(c: char) -> bool {
|
||||
c == sep || c == sep2
|
||||
pub fn is_sep_byte(u: &u8) -> bool {
|
||||
*u as char == sep || *u as char == sep2
|
||||
}
|
||||
|
||||
/// Returns whether the given byte is a path separator.
|
||||
/// Only allows the primary separator '\'; use is_sep_byte to allow '/'.
|
||||
#[inline]
|
||||
pub fn is_sep_byte_verbatim(u: &u8) -> bool {
|
||||
*u as char == sep
|
||||
}
|
||||
|
||||
/// Prefix types for Path
|
||||
|
|
@ -953,7 +986,7 @@ pub fn parse_prefix<'a>(mut path: &'a str) -> Option<PathPrefix> {
|
|||
if path.starts_with("UNC\\") {
|
||||
// \\?\UNC\server\share
|
||||
path = path.slice_from(4);
|
||||
let (idx_a, idx_b) = match parse_two_comps(path, is_sep) {
|
||||
let (idx_a, idx_b) = match parse_two_comps(path, is_sep_verbatim) {
|
||||
Some(x) => x,
|
||||
None => (path.len(), 0)
|
||||
};
|
||||
|
|
@ -977,7 +1010,7 @@ pub fn parse_prefix<'a>(mut path: &'a str) -> Option<PathPrefix> {
|
|||
let idx = path.find('\\').unwrap_or(path.len());
|
||||
return Some(DeviceNSPrefix(idx));
|
||||
}
|
||||
match parse_two_comps(path, is_sep2) {
|
||||
match parse_two_comps(path, is_sep) {
|
||||
Some((idx_a, idx_b)) if idx_a > 0 && idx_b > 0 => {
|
||||
// \\server\share
|
||||
return Some(UNCPrefix(idx_a, idx_b));
|
||||
|
|
@ -1006,14 +1039,14 @@ pub fn parse_prefix<'a>(mut path: &'a str) -> Option<PathPrefix> {
|
|||
|
||||
// None result means the string didn't need normalizing
|
||||
fn normalize_helper<'a>(s: &'a str, prefix: Option<PathPrefix>) -> (bool,Option<~[&'a str]>) {
|
||||
let f = if !prefix_is_verbatim(prefix) { is_sep2 } else { is_sep };
|
||||
let f = if !prefix_is_verbatim(prefix) { is_sep } else { is_sep_verbatim };
|
||||
let is_abs = s.len() > prefix_len(prefix) && f(s.char_at(prefix_len(prefix)));
|
||||
let s_ = s.slice_from(prefix_len(prefix));
|
||||
let s_ = if is_abs { s_.slice_from(1) } else { s_ };
|
||||
|
||||
if is_abs && s_.is_empty() {
|
||||
return (is_abs, match prefix {
|
||||
Some(DiskPrefix) | None => (if is_sep(s.char_at(prefix_len(prefix))) { None }
|
||||
Some(DiskPrefix) | None => (if is_sep_verbatim(s.char_at(prefix_len(prefix))) { None }
|
||||
else { Some(~[]) }),
|
||||
Some(_) => Some(~[]), // need to trim the trailing separator
|
||||
});
|
||||
|
|
@ -1036,7 +1069,7 @@ fn normalize_helper<'a>(s: &'a str, prefix: Option<PathPrefix>) -> (bool,Option<
|
|||
} else { comps.push(comp) }
|
||||
}
|
||||
if !changed && !prefix_is_verbatim(prefix) {
|
||||
changed = s.find(is_sep2).is_some();
|
||||
changed = s.find(is_sep).is_some();
|
||||
}
|
||||
if changed {
|
||||
if comps.is_empty() && !is_abs && prefix.is_none() {
|
||||
|
|
@ -1078,8 +1111,8 @@ fn prefix_len(p: Option<PathPrefix>) -> uint {
|
|||
}
|
||||
|
||||
fn prefix_is_sep(p: Option<PathPrefix>, c: u8) -> bool {
|
||||
c.is_ascii() && if !prefix_is_verbatim(p) { is_sep2(c as char) }
|
||||
else { is_sep(c as char) }
|
||||
c.is_ascii() && if !prefix_is_verbatim(p) { is_sep(c as char) }
|
||||
else { is_sep_verbatim(c as char) }
|
||||
}
|
||||
|
||||
// Stat support
|
||||
|
|
@ -1636,9 +1669,9 @@ mod tests {
|
|||
|
||||
// we do want to check one odd case though to ensure the prefix is re-parsed
|
||||
let mut p = Path::new("\\\\?\\C:");
|
||||
assert_eq!(p.prefix(), Some(VerbatimPrefix(2)));
|
||||
assert_eq!(prefix(&p), Some(VerbatimPrefix(2)));
|
||||
p.push("foo");
|
||||
assert_eq!(p.prefix(), Some(VerbatimDiskPrefix));
|
||||
assert_eq!(prefix(&p), Some(VerbatimDiskPrefix));
|
||||
assert_eq!(p.as_str(), Some("\\\\?\\C:\\foo"));
|
||||
|
||||
// and another with verbatim non-normalized paths
|
||||
|
|
@ -1654,7 +1687,7 @@ mod tests {
|
|||
{
|
||||
let mut p = Path::new($path);
|
||||
let push = Path::new($push);
|
||||
p.push_path(&push);
|
||||
p.push(&push);
|
||||
assert_eq!(p.as_str(), Some($exp));
|
||||
}
|
||||
)
|
||||
|
|
@ -1837,7 +1870,7 @@ mod tests {
|
|||
{
|
||||
let path = Path::new($path);
|
||||
let join = Path::new($join);
|
||||
let res = path.join_path(&join);
|
||||
let res = path.join(&join);
|
||||
assert_eq!(res.as_str(), Some($exp));
|
||||
}
|
||||
)
|
||||
|
|
@ -1849,7 +1882,7 @@ mod tests {
|
|||
t!(s: "a\\b", "\\c\\d", "\\c\\d");
|
||||
t!(s: ".", "a\\b", "a\\b");
|
||||
t!(s: "\\", "a\\b", "\\a\\b");
|
||||
// join_path is implemented using push_path, so there's no need for
|
||||
// join is implemented using push, so there's no need for
|
||||
// the full set of prefix tests
|
||||
}
|
||||
|
||||
|
|
@ -2217,11 +2250,11 @@ mod tests {
|
|||
let b = path.is_absolute();
|
||||
assert!(b == abs, "Path '{}'.is_absolute(): expected {:?}, found {:?}",
|
||||
path.as_str().unwrap(), abs, b);
|
||||
let b = path.is_vol_relative();
|
||||
assert!(b == vol, "Path '{}'.is_vol_relative(): expected {:?}, found {:?}",
|
||||
let b = is_vol_relative(&path);
|
||||
assert!(b == vol, "is_vol_relative('{}'): expected {:?}, found {:?}",
|
||||
path.as_str().unwrap(), vol, b);
|
||||
let b = path.is_cwd_relative();
|
||||
assert!(b == cwd, "Path '{}'.is_cwd_relative(): expected {:?}, found {:?}",
|
||||
let b = is_cwd_relative(&path);
|
||||
assert!(b == cwd, "is_cwd_relative('{}'): expected {:?}, found {:?}",
|
||||
path.as_str().unwrap(), cwd, b);
|
||||
let b = path.is_relative();
|
||||
assert!(b == rel, "Path '{}'.is_relativf(): expected {:?}, found {:?}",
|
||||
|
|
@ -2614,54 +2647,4 @@ mod tests {
|
|||
t!(s: ".", [b!(".")]);
|
||||
// since this is really a wrapper around str_component_iter, those tests suffice
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_each_parent() {
|
||||
assert!(Path::new("/foo/bar").each_parent(|_| true));
|
||||
assert!(!Path::new("/foo/bar").each_parent(|_| false));
|
||||
|
||||
macro_rules! t(
|
||||
(s: $path:expr, $exp:expr) => (
|
||||
{
|
||||
let path = Path::new($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"]);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue