auto merge of #11642 : erickt/rust/path, r=huonw

This pull request exposes a platform independent way to get the path separator. This is useful when building complicated paths by hand.
This commit is contained in:
bors 2014-01-19 13:11:37 -08:00
commit 18061e85b7
3 changed files with 62 additions and 41 deletions

View File

@ -117,6 +117,20 @@ pub use StrComponents = self::windows::StrComponents;
#[cfg(windows)] #[cfg(windows)]
pub use RevStrComponents = self::windows::RevStrComponents; pub use RevStrComponents = self::windows::RevStrComponents;
/// Alias for the platform-native separator character.
#[cfg(unix)]
pub use SEP = self::posix::SEP;
/// Alias for the platform-native separator byte.
#[cfg(windows)]
pub use SEP = self::windows::SEP;
/// Alias for the platform-native separator character.
#[cfg(unix)]
pub use SEP_BYTE = self::posix::SEP_BYTE;
/// Alias for the platform-native separator byte.
#[cfg(windows)]
pub use SEP_BYTE = self::windows::SEP_BYTE;
/// Typedef for the platform-native separator char func /// Typedef for the platform-native separator char func
#[cfg(unix)] #[cfg(unix)]
pub use is_sep = self::posix::is_sep; pub use is_sep = self::posix::is_sep;

View File

@ -45,19 +45,21 @@ pub struct Path {
} }
/// The standard path separator character /// The standard path separator character
pub static sep: char = '/'; pub static SEP: char = '/';
static sep_byte: u8 = sep as u8;
/// The standard path separator byte
pub static SEP_BYTE: u8 = SEP as u8;
/// Returns whether the given byte is a path separator /// Returns whether the given byte is a path separator
#[inline] #[inline]
pub fn is_sep_byte(u: &u8) -> bool { pub fn is_sep_byte(u: &u8) -> bool {
*u as char == sep *u as char == SEP
} }
/// Returns whether the given char is a path separator /// Returns whether the given char is a path separator
#[inline] #[inline]
pub fn is_sep(c: char) -> bool { pub fn is_sep(c: char) -> bool {
c == sep c == SEP
} }
impl Eq for Path { impl Eq for Path {
@ -115,7 +117,7 @@ impl GenericPathUnsafe for Path {
unsafe fn new_unchecked<T: BytesContainer>(path: T) -> Path { unsafe fn new_unchecked<T: BytesContainer>(path: T) -> Path {
let path = Path::normalize(path.container_as_bytes()); let path = Path::normalize(path.container_as_bytes());
assert!(!path.is_empty()); assert!(!path.is_empty());
let idx = path.rposition_elem(&sep_byte); let idx = path.rposition_elem(&SEP_BYTE);
Path{ repr: path, sepidx: idx } Path{ repr: path, sepidx: idx }
} }
@ -125,7 +127,7 @@ impl GenericPathUnsafe for Path {
None if bytes!("..") == self.repr => { None if bytes!("..") == self.repr => {
let mut v = vec::with_capacity(3 + filename.len()); let mut v = vec::with_capacity(3 + filename.len());
v.push_all(dot_dot_static); v.push_all(dot_dot_static);
v.push(sep_byte); v.push(SEP_BYTE);
v.push_all(filename); v.push_all(filename);
self.repr = Path::normalize(v); self.repr = Path::normalize(v);
} }
@ -135,7 +137,7 @@ impl GenericPathUnsafe for Path {
Some(idx) if self.repr.slice_from(idx+1) == bytes!("..") => { Some(idx) if self.repr.slice_from(idx+1) == bytes!("..") => {
let mut v = vec::with_capacity(self.repr.len() + 1 + filename.len()); let mut v = vec::with_capacity(self.repr.len() + 1 + filename.len());
v.push_all(self.repr); v.push_all(self.repr);
v.push(sep_byte); v.push(SEP_BYTE);
v.push_all(filename); v.push_all(filename);
self.repr = Path::normalize(v); self.repr = Path::normalize(v);
} }
@ -146,22 +148,22 @@ impl GenericPathUnsafe for Path {
self.repr = Path::normalize(v); self.repr = Path::normalize(v);
} }
} }
self.sepidx = self.repr.rposition_elem(&sep_byte); self.sepidx = self.repr.rposition_elem(&SEP_BYTE);
} }
unsafe fn push_unchecked<T: BytesContainer>(&mut self, path: T) { unsafe fn push_unchecked<T: BytesContainer>(&mut self, path: T) {
let path = path.container_as_bytes(); let path = path.container_as_bytes();
if !path.is_empty() { if !path.is_empty() {
if path[0] == sep_byte { if path[0] == SEP_BYTE {
self.repr = Path::normalize(path); self.repr = Path::normalize(path);
} else { } else {
let mut v = vec::with_capacity(self.repr.len() + path.len() + 1); let mut v = vec::with_capacity(self.repr.len() + path.len() + 1);
v.push_all(self.repr); v.push_all(self.repr);
v.push(sep_byte); v.push(SEP_BYTE);
v.push_all(path); v.push_all(path);
self.repr = Path::normalize(v); self.repr = Path::normalize(v);
} }
self.sepidx = self.repr.rposition_elem(&sep_byte); self.sepidx = self.repr.rposition_elem(&SEP_BYTE);
} }
} }
} }
@ -211,7 +213,7 @@ impl GenericPath for Path {
} else { } else {
self.repr.truncate(idx); self.repr.truncate(idx);
} }
self.sepidx = self.repr.rposition_elem(&sep_byte); self.sepidx = self.repr.rposition_elem(&SEP_BYTE);
true true
} }
} }
@ -227,7 +229,7 @@ impl GenericPath for Path {
#[inline] #[inline]
fn is_absolute(&self) -> bool { fn is_absolute(&self) -> bool {
self.repr[0] == sep_byte self.repr[0] == SEP_BYTE
} }
fn is_ancestor_of(&self, other: &Path) -> bool { fn is_ancestor_of(&self, other: &Path) -> bool {
@ -291,7 +293,7 @@ impl GenericPath for Path {
} }
} }
} }
Some(Path::new(comps.connect_vec(&sep_byte))) Some(Path::new(comps.connect_vec(&SEP_BYTE)))
} }
} }
@ -333,14 +335,14 @@ impl Path {
fn normalize<V: Vector<u8>+CopyableVector<u8>>(v: V) -> ~[u8] { fn normalize<V: Vector<u8>+CopyableVector<u8>>(v: V) -> ~[u8] {
// borrowck is being very picky // borrowck is being very picky
let val = { let val = {
let is_abs = !v.as_slice().is_empty() && v.as_slice()[0] == sep_byte; 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 v_ = if is_abs { v.as_slice().slice_from(1) } else { v.as_slice() };
let comps = normalize_helper(v_, is_abs); let comps = normalize_helper(v_, is_abs);
match comps { match comps {
None => None, None => None,
Some(comps) => { Some(comps) => {
if is_abs && comps.is_empty() { if is_abs && comps.is_empty() {
Some(~[sep_byte]) Some(~[SEP_BYTE])
} else { } else {
let n = if is_abs { comps.len() } else { comps.len() - 1} + let n = if is_abs { comps.len() } else { comps.len() - 1} +
comps.iter().map(|v| v.len()).sum(); comps.iter().map(|v| v.len()).sum();
@ -353,7 +355,7 @@ impl Path {
} }
} }
for comp in it { for comp in it {
v.push(sep_byte); v.push(SEP_BYTE);
v.push_all(comp); v.push_all(comp);
} }
Some(v) Some(v)
@ -372,7 +374,7 @@ impl Path {
/// /a/b/c and a/b/c yield the same set of components. /// /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. /// A path of "/" yields no components. A path of "." yields one component.
pub fn components<'a>(&'a self) -> Components<'a> { pub fn components<'a>(&'a self) -> Components<'a> {
let v = if self.repr[0] == sep_byte { let v = if self.repr[0] == SEP_BYTE {
self.repr.slice_from(1) self.repr.slice_from(1)
} else { self.repr.as_slice() }; } else { self.repr.as_slice() };
let mut ret = v.split(is_sep_byte); let mut ret = v.split(is_sep_byte);
@ -386,7 +388,7 @@ impl Path {
/// Returns an iterator that yields each component of the path in reverse. /// Returns an iterator that yields each component of the path in reverse.
/// See components() for details. /// See components() for details.
pub fn rev_components<'a>(&'a self) -> RevComponents<'a> { pub fn rev_components<'a>(&'a self) -> RevComponents<'a> {
let v = if self.repr[0] == sep_byte { let v = if self.repr[0] == SEP_BYTE {
self.repr.slice_from(1) self.repr.slice_from(1)
} else { self.repr.as_slice() }; } else { self.repr.as_slice() };
let mut ret = v.rsplit(is_sep_byte); let mut ret = v.rsplit(is_sep_byte);

View File

@ -183,7 +183,7 @@ impl GenericPathUnsafe for Path {
None if ".." == self.repr => { None if ".." == self.repr => {
let mut s = str::with_capacity(3 + filename.len()); let mut s = str::with_capacity(3 + filename.len());
s.push_str(".."); s.push_str("..");
s.push_char(sep); s.push_char(SEP);
s.push_str(filename); s.push_str(filename);
self.update_normalized(s); self.update_normalized(s);
} }
@ -193,7 +193,7 @@ impl GenericPathUnsafe for Path {
Some((_,idxa,end)) if self.repr.slice(idxa,end) == ".." => { Some((_,idxa,end)) if self.repr.slice(idxa,end) == ".." => {
let mut s = str::with_capacity(end + 1 + filename.len()); let mut s = str::with_capacity(end + 1 + filename.len());
s.push_str(self.repr.slice_to(end)); s.push_str(self.repr.slice_to(end));
s.push_char(sep); s.push_char(SEP);
s.push_str(filename); s.push_str(filename);
self.update_normalized(s); self.update_normalized(s);
} }
@ -206,7 +206,7 @@ impl GenericPathUnsafe for Path {
Some((idxb,_,_)) => { Some((idxb,_,_)) => {
let mut s = str::with_capacity(idxb + 1 + filename.len()); let mut s = str::with_capacity(idxb + 1 + filename.len());
s.push_str(self.repr.slice_to(idxb)); s.push_str(self.repr.slice_to(idxb));
s.push_char(sep); s.push_char(SEP);
s.push_str(filename); s.push_str(filename);
self.update_normalized(s); self.update_normalized(s);
} }
@ -264,8 +264,8 @@ impl GenericPathUnsafe for Path {
// if me is "C:" we don't want to add a path separator // if me is "C:" we don't want to add a path separator
match me.prefix { match me.prefix {
Some(DiskPrefix) if me.repr.len() == plen => (), Some(DiskPrefix) if me.repr.len() == plen => (),
_ if !(me.repr.len() > plen && me.repr[me.repr.len()-1] == sep as u8) => { _ if !(me.repr.len() > plen && me.repr[me.repr.len()-1] == SEP_BYTE) => {
s.push_char(sep); s.push_char(SEP);
} }
_ => () _ => ()
} }
@ -460,7 +460,7 @@ impl GenericPath for Path {
match self.prefix { match self.prefix {
Some(DiskPrefix) => { Some(DiskPrefix) => {
let rest = self.repr.slice_from(self.prefix_len()); let rest = self.repr.slice_from(self.prefix_len());
rest.len() > 0 && rest[0] == sep as u8 rest.len() > 0 && rest[0] == SEP_BYTE
} }
Some(_) => true, Some(_) => true,
None => false None => false
@ -501,7 +501,7 @@ impl GenericPath for Path {
fn path_relative_from(&self, base: &Path) -> Option<Path> { fn path_relative_from(&self, base: &Path) -> Option<Path> {
fn comp_requires_verbatim(s: &str) -> bool { fn comp_requires_verbatim(s: &str) -> bool {
s == "." || s == ".." || s.contains_char(sep2) s == "." || s == ".." || s.contains_char(SEP2)
} }
if !self.equiv_prefix(base) { if !self.equiv_prefix(base) {
@ -619,14 +619,14 @@ impl Path {
let s = match self.prefix { let s = match self.prefix {
Some(_) => { Some(_) => {
let plen = self.prefix_len(); let plen = self.prefix_len();
if self.repr.len() > plen && self.repr[plen] == sep as u8 { if self.repr.len() > plen && self.repr[plen] == SEP_BYTE {
self.repr.slice_from(plen+1) self.repr.slice_from(plen+1)
} else { self.repr.slice_from(plen) } } else { self.repr.slice_from(plen) }
} }
None if self.repr[0] == sep as u8 => self.repr.slice_from(1), None if self.repr[0] == SEP_BYTE => self.repr.slice_from(1),
None => self.repr.as_slice() None => self.repr.as_slice()
}; };
let ret = s.split_terminator(sep).map(Some); let ret = s.split_terminator(SEP).map(Some);
ret ret
} }
@ -703,7 +703,7 @@ impl Path {
Some(VerbatimUNCPrefix(x, 0)) if s.len() == 8 + x => { Some(VerbatimUNCPrefix(x, 0)) if s.len() == 8 + x => {
// the server component has no trailing '\' // the server component has no trailing '\'
let mut s = s.into_owned(); let mut s = s.into_owned();
s.push_char(sep); s.push_char(SEP);
Some(s) Some(s)
} }
_ => None _ => None
@ -739,7 +739,7 @@ impl Path {
if is_abs { if is_abs {
// normalize C:/ to C:\ // normalize C:/ to C:\
unsafe { unsafe {
str::raw::as_owned_vec(&mut s)[2] = sep as u8; str::raw::as_owned_vec(&mut s)[2] = SEP_BYTE;
} }
} }
Some(s) Some(s)
@ -761,7 +761,7 @@ impl Path {
} }
} }
} else if is_abs && comps.is_empty() { } else if is_abs && comps.is_empty() {
Some(str::from_char(sep)) Some(str::from_char(SEP))
} else { } else {
let prefix_ = s.slice_to(prefix_len(prefix)); let prefix_ = s.slice_to(prefix_len(prefix));
let n = prefix_.len() + let n = prefix_.len() +
@ -781,7 +781,7 @@ impl Path {
Some(UNCPrefix(a,b)) => { Some(UNCPrefix(a,b)) => {
s.push_str("\\\\"); s.push_str("\\\\");
s.push_str(prefix_.slice(2, a+2)); s.push_str(prefix_.slice(2, a+2));
s.push_char(sep); s.push_char(SEP);
s.push_str(prefix_.slice(3+a, 3+a+b)); s.push_str(prefix_.slice(3+a, 3+a+b));
} }
Some(_) => s.push_str(prefix_), Some(_) => s.push_str(prefix_),
@ -795,7 +795,7 @@ impl Path {
} }
} }
for comp in it { for comp in it {
s.push_char(sep); s.push_char(SEP);
s.push_str(comp); s.push_str(comp);
} }
Some(s) Some(s)
@ -837,7 +837,7 @@ impl Path {
fn has_nonsemantic_trailing_slash(&self) -> bool { fn has_nonsemantic_trailing_slash(&self) -> bool {
is_verbatim(self) && 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 self.repr[self.repr.len()-1] == SEP_BYTE
} }
fn update_normalized<S: Str>(&mut self, s: S) { fn update_normalized<S: Str>(&mut self, s: S) {
@ -877,36 +877,41 @@ pub fn is_verbatim(path: &Path) -> bool {
} }
/// The standard path separator character /// The standard path separator character
pub static sep: char = '\\'; pub static SEP: char = '\\';
/// The standard path separator byte
pub static SEP_BYTE: u8 = SEP as u8;
/// The alternative path separator character /// The alternative path separator character
pub static sep2: char = '/'; pub static SEP2: char = '/';
/// The alternative path separator character
pub static SEP2_BYTE: u8 = SEP2 as u8;
/// Returns whether the given char is a path separator. /// Returns whether the given char is a path separator.
/// Allows both the primary separator '\' and the alternative separator '/'. /// Allows both the primary separator '\' and the alternative separator '/'.
#[inline] #[inline]
pub fn is_sep(c: char) -> bool { pub fn is_sep(c: char) -> bool {
c == sep || c == sep2 c == SEP || c == SEP2
} }
/// Returns whether the given char is a path separator. /// Returns whether the given char is a path separator.
/// Only allows the primary separator '\'; use is_sep to allow '/'. /// Only allows the primary separator '\'; use is_sep to allow '/'.
#[inline] #[inline]
pub fn is_sep_verbatim(c: char) -> bool { pub fn is_sep_verbatim(c: char) -> bool {
c == sep c == SEP
} }
/// Returns whether the given byte is a path separator. /// Returns whether the given byte is a path separator.
/// Allows both the primary separator '\' and the alternative separator '/'. /// Allows both the primary separator '\' and the alternative separator '/'.
#[inline] #[inline]
pub fn is_sep_byte(u: &u8) -> bool { pub fn is_sep_byte(u: &u8) -> bool {
*u as char == sep || *u as char == sep2 *u == SEP_BYTE || *u == SEP2_BYTE
} }
/// Returns whether the given byte is a path separator. /// Returns whether the given byte is a path separator.
/// Only allows the primary separator '\'; use is_sep_byte to allow '/'. /// Only allows the primary separator '\'; use is_sep_byte to allow '/'.
#[inline] #[inline]
pub fn is_sep_byte_verbatim(u: &u8) -> bool { pub fn is_sep_byte_verbatim(u: &u8) -> bool {
*u as char == sep *u == SEP_BYTE
} }
/// Prefix types for Path /// Prefix types for Path