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:
Kevin Ballard 2013-10-07 19:16:58 -07:00
parent c01a97b7a9
commit bab7eb20df
17 changed files with 274 additions and 390 deletions

View file

@ -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

View file

@ -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());
}
}

View file

@ -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!(".")]);
}
}

View file

@ -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"]);
}
}