diff options
| author | Kevin Ballard <kevin@sb.org> | 2013-10-07 19:16:58 -0700 |
|---|---|---|
| committer | Kevin Ballard <kevin@sb.org> | 2013-10-16 10:26:48 -0700 |
| commit | bab7eb20dff32294c65fa28cece552481c40cf0b (patch) | |
| tree | fc81daee54a1f01c62c792ccbfba362e61d52d76 /src/libstd/path | |
| parent | c01a97b7a981fb5ae008be7e06df4bf6a85eba4f (diff) | |
| download | rust-bab7eb20dff32294c65fa28cece552481c40cf0b.tar.gz rust-bab7eb20dff32294c65fa28cece552481c40cf0b.zip | |
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.
Diffstat (limited to 'src/libstd/path')
| -rw-r--r-- | src/libstd/path/mod.rs | 156 | ||||
| -rw-r--r-- | src/libstd/path/posix.rs | 131 | ||||
| -rw-r--r-- | src/libstd/path/windows.rs | 247 |
3 files changed, 217 insertions, 317 deletions
diff --git a/src/libstd/path/mod.rs b/src/libstd/path/mod.rs index ff370e9d7c1..278a4ab36ff 100644 --- a/src/libstd/path/mod.rs +++ b/src/libstd/path/mod.rs @@ -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()); } } diff --git a/src/libstd/path/posix.rs b/src/libstd/path/posix.rs index 8acc1346bcc..fdc943fb882 100644 --- a/src/libstd/path/posix.rs +++ b/src/libstd/path/posix.rs @@ -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!(".")]); - } } diff --git a/src/libstd/path/windows.rs b/src/libstd/path/windows.rs index 86c4bb6f8a3..5f8e1dc58fa 100644 --- a/src/libstd/path/windows.rs +++ b/src/libstd/path/windows.rs @@ -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); @@ -419,11 +457,6 @@ impl GenericPath for Path { } #[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"]); - } } |
