| // Copyright 2012 The Rust Project Developers. See the COPYRIGHT |
| // file at the top-level directory of this distribution and at |
| // http://rust-lang.org/COPYRIGHT. |
| // |
| // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or |
| // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license |
| // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your |
| // option. This file may not be copied, modified, or distributed |
| // except according to those terms. |
| |
| /*! |
| |
| Cross-platform file path handling |
| |
| */ |
| |
| #[allow(missing_doc)]; |
| |
| use c_str::ToCStr; |
| use c_str; |
| use clone::Clone; |
| use cmp::Eq; |
| use container::Container; |
| use iter::{Iterator, range}; |
| use libc; |
| use num; |
| use option::{None, Option, Some}; |
| use str::{OwnedStr, Str, StrSlice, StrVector}; |
| use to_str::ToStr; |
| use ascii::{AsciiCast, AsciiStr}; |
| use vec::{Vector, OwnedVector, ImmutableVector, OwnedCopyableVector}; |
| |
| #[cfg(windows)] |
| pub use Path = self::WindowsPath; |
| #[cfg(unix)] |
| pub use Path = self::PosixPath; |
| |
| #[deriving(Clone, Eq)] |
| pub struct WindowsPath { |
| host: Option<~str>, |
| device: Option<~str>, |
| is_absolute: bool, |
| components: ~[~str], |
| } |
| |
| pub fn WindowsPath(s: &str) -> WindowsPath { |
| GenericPath::from_str(s) |
| } |
| |
| #[deriving(Clone, Eq)] |
| pub struct PosixPath { |
| is_absolute: bool, |
| components: ~[~str], |
| } |
| |
| pub fn PosixPath(s: &str) -> PosixPath { |
| GenericPath::from_str(s) |
| } |
| |
| pub trait GenericPath : Clone + Eq + ToStr { |
| /// Converts a string to a path. |
| fn from_str(&str) -> Self; |
| |
| /// Returns the directory component of `self`, as a string. |
| fn dirname(&self) -> ~str { |
| let s = self.dir_path().to_str(); |
| match s.len() { |
| 0 => ~".", |
| _ => s, |
| } |
| } |
| |
| /// Returns the file component of `self`, as a string option. |
| /// Returns None if `self` names a directory. |
| fn filename<'a>(&'a self) -> Option<&'a str> { |
| match self.components().len() { |
| 0 => None, |
| n => Some(self.components()[n - 1].as_slice()), |
| } |
| } |
| |
| /// Returns the stem of the file component of `self`, as a string option. |
| /// The stem is the slice of a filename starting at 0 and ending just before |
| /// the last '.' in the name. |
| /// Returns None if `self` names a directory. |
| fn filestem<'a>(&'a self) -> Option<&'a str> { |
| match self.filename() { |
| None => None, |
| Some(ref f) => { |
| match f.rfind('.') { |
| Some(p) => Some(f.slice_to(p)), |
| None => Some((*f)), |
| } |
| } |
| } |
| } |
| |
| /// Returns the type of the file component of `self`, as a string option. |
| /// The file type is the slice of a filename starting just after the last |
| /// '.' in the name and ending at the last index in the filename. |
| /// Returns None if `self` names a directory. |
| fn filetype<'a>(&'a self) -> Option<&'a str> { |
| match self.filename() { |
| None => None, |
| Some(ref f) => { |
| match f.rfind('.') { |
| Some(p) if p < f.len() => Some(f.slice_from(p)), |
| _ => None, |
| } |
| } |
| } |
| } |
| |
| /// Returns a new path consisting of `self` with the parent directory component replaced |
| /// with the given string. |
| fn with_dirname(&self, (&str)) -> Self; |
| |
| /// Returns a new path consisting of `self` with the file component replaced |
| /// with the given string. |
| fn with_filename(&self, (&str)) -> Self; |
| |
| /// Returns a new path consisting of `self` with the file stem replaced |
| /// with the given string. |
| fn with_filestem(&self, s: &str) -> Self { |
| match self.filetype() { |
| None => self.with_filename(s), |
| Some(ref t) => self.with_filename(s.to_owned() + *t), |
| } |
| } |
| |
| /// Returns a new path consisting of `self` with the file type replaced |
| /// with the given string. |
| fn with_filetype(&self, t: &str) -> Self { |
| match (t.len(), self.filestem()) { |
| (0, None) => (*self).clone(), |
| (0, Some(ref s)) => self.with_filename(*s), |
| (_, None) => self.with_filename(fmt!(".%s", t)), |
| (_, Some(ref s)) => self.with_filename(fmt!("%s.%s", *s, t)), |
| } |
| } |
| |
| /// Returns the directory component of `self`, as a new path. |
| /// If `self` has no parent, returns `self`. |
| fn dir_path(&self) -> Self { |
| match self.components().len() { |
| 0 => (*self).clone(), |
| _ => self.pop(), |
| } |
| } |
| |
| /// Returns the file component of `self`, as a new path. |
| /// If `self` names a directory, returns the empty path. |
| fn file_path(&self) -> Self; |
| |
| /// Returns a new path whose parent directory is `self` and whose |
| /// file component is the given string. |
| fn push(&self, (&str)) -> Self; |
| |
| /// Returns a new path consisting of the given path, made relative to `self`. |
| fn push_rel(&self, other: &Self) -> Self { |
| assert!(!other.is_absolute()); |
| self.push_many(other.components()) |
| } |
| |
| /// Returns a new path consisting of the path given by the given vector |
| /// of strings, relative to `self`. |
| fn push_many<S: Str>(&self, (&[S])) -> Self; |
| |
| /// Identical to `dir_path` except in the case where `self` has only one |
| /// component. In this case, `pop` returns the empty path. |
| fn pop(&self) -> Self; |
| |
| /// The same as `push_rel`, except that the directory argument must not |
| /// contain directory separators in any of its components. |
| fn unsafe_join(&self, (&Self)) -> Self; |
| |
| /// On Unix, always returns `false`. On Windows, returns `true` iff `self`'s |
| /// file stem is one of: `con` `aux` `com1` `com2` `com3` `com4` |
| /// `lpt1` `lpt2` `lpt3` `prn` `nul`. |
| fn is_restricted(&self) -> bool; |
| |
| /// Returns a new path that names the same file as `self`, without containing |
| /// any '.', '..', or empty components. On Windows, uppercases the drive letter |
| /// as well. |
| fn normalize(&self) -> Self; |
| |
| /// Returns `true` if `self` is an absolute path. |
| fn is_absolute(&self) -> bool; |
| |
| /// True if `self` is an ancestor of `other`. |
| // See `test_is_ancestor_of` for examples. |
| fn is_ancestor_of(&self, other: &Self) -> bool { |
| debug!("%s / %s %? %?", self.to_str(), other.to_str(), self.is_absolute(), |
| self.components().len()); |
| self == other || |
| (!other.components().is_empty() && |
| !(self.components().is_empty() && !self.is_absolute()) && |
| self.is_ancestor_of(&other.pop())) |
| } |
| |
| /// Finds the relative path from one file to another. |
| fn get_relative_to(&self, abs2: (&Self)) -> Self { |
| assert!(self.is_absolute()); |
| assert!(abs2.is_absolute()); |
| let abs1 = self.normalize(); |
| let abs2 = abs2.normalize(); |
| |
| let split1: &[~str] = abs1.components(); |
| let split2: &[~str] = abs2.components(); |
| let len1 = split1.len(); |
| let len2 = split2.len(); |
| assert!(len1 > 0); |
| assert!(len2 > 0); |
| |
| let max_common_path = num::min(len1, len2) - 1; |
| let mut start_idx = 0; |
| while start_idx < max_common_path |
| && split1[start_idx] == split2[start_idx] { |
| start_idx += 1; |
| } |
| |
| let mut path: ~[~str] = ~[]; |
| for _ in range(start_idx, len1 - 1) { path.push(~".."); }; |
| |
| path.push_all(split2.slice(start_idx, len2 - 1)); |
| |
| let mut result: Self = GenericPath::from_str("."); |
| if !path.is_empty() { |
| // Without this type hint, the typechecker doesn't seem to like it |
| let p: Self = GenericPath::from_str(""); |
| result = p.push_many(path); |
| }; |
| result |
| } |
| |
| |
| /// Returns `true` iff `child` is a suffix of `parent`. See the test |
| /// case for examples. |
| fn is_parent_of(&self, child: &Self) -> bool { |
| if !self.is_absolute() || child.is_absolute() |
| || self.components().len() < child.components().len() |
| || self.components().is_empty() { |
| return false; |
| } |
| let child_components = child.components().len(); |
| let parent_components = self.components().len(); |
| let to_drop = self.components().len() - child_components; |
| self.components().slice(to_drop, parent_components) == child.components() |
| } |
| |
| fn components<'a>(&'a self) -> &'a [~str]; |
| } |
| |
| #[cfg(target_os = "linux")] |
| #[cfg(target_os = "android")] |
| mod stat { |
| #[cfg(target_arch = "x86")] |
| pub mod arch { |
| use libc; |
| |
| pub fn default_stat() -> libc::stat { |
| libc::stat { |
| st_dev: 0, |
| __pad1: 0, |
| st_ino: 0, |
| st_mode: 0, |
| st_nlink: 0, |
| st_uid: 0, |
| st_gid: 0, |
| st_rdev: 0, |
| __pad2: 0, |
| st_size: 0, |
| st_blksize: 0, |
| st_blocks: 0, |
| st_atime: 0, |
| st_atime_nsec: 0, |
| st_mtime: 0, |
| st_mtime_nsec: 0, |
| st_ctime: 0, |
| st_ctime_nsec: 0, |
| __unused4: 0, |
| __unused5: 0, |
| } |
| } |
| } |
| |
| #[cfg(target_arch = "arm")] |
| pub mod arch { |
| use libc; |
| |
| pub fn default_stat() -> libc::stat { |
| libc::stat { |
| st_dev: 0, |
| __pad0: [0, ..4], |
| __st_ino: 0, |
| st_mode: 0, |
| st_nlink: 0, |
| st_uid: 0, |
| st_gid: 0, |
| st_rdev: 0, |
| __pad3: [0, ..4], |
| st_size: 0, |
| st_blksize: 0, |
| st_blocks: 0, |
| st_atime: 0, |
| st_atime_nsec: 0, |
| st_mtime: 0, |
| st_mtime_nsec: 0, |
| st_ctime: 0, |
| st_ctime_nsec: 0, |
| st_ino: 0 |
| } |
| } |
| } |
| |
| #[cfg(target_arch = "mips")] |
| pub mod arch { |
| use libc; |
| |
| pub fn default_stat() -> libc::stat { |
| libc::stat { |
| st_dev: 0, |
| st_pad1: [0, ..3], |
| st_ino: 0, |
| st_mode: 0, |
| st_nlink: 0, |
| st_uid: 0, |
| st_gid: 0, |
| st_rdev: 0, |
| st_pad2: [0, ..2], |
| st_size: 0, |
| st_pad3: 0, |
| st_atime: 0, |
| st_atime_nsec: 0, |
| st_mtime: 0, |
| st_mtime_nsec: 0, |
| st_ctime: 0, |
| st_ctime_nsec: 0, |
| st_blksize: 0, |
| st_blocks: 0, |
| st_pad5: [0, ..14], |
| } |
| } |
| } |
| |
| #[cfg(target_arch = "x86_64")] |
| pub mod arch { |
| use libc; |
| |
| pub fn default_stat() -> libc::stat { |
| libc::stat { |
| st_dev: 0, |
| st_ino: 0, |
| st_nlink: 0, |
| st_mode: 0, |
| st_uid: 0, |
| st_gid: 0, |
| __pad0: 0, |
| st_rdev: 0, |
| st_size: 0, |
| st_blksize: 0, |
| st_blocks: 0, |
| st_atime: 0, |
| st_atime_nsec: 0, |
| st_mtime: 0, |
| st_mtime_nsec: 0, |
| st_ctime: 0, |
| st_ctime_nsec: 0, |
| __unused: [0, 0, 0], |
| } |
| } |
| } |
| } |
| |
| #[cfg(target_os = "freebsd")] |
| mod stat { |
| #[cfg(target_arch = "x86_64")] |
| pub mod arch { |
| use libc; |
| |
| pub fn default_stat() -> libc::stat { |
| libc::stat { |
| st_dev: 0, |
| st_ino: 0, |
| st_mode: 0, |
| st_nlink: 0, |
| st_uid: 0, |
| st_gid: 0, |
| st_rdev: 0, |
| st_atime: 0, |
| st_atime_nsec: 0, |
| st_mtime: 0, |
| st_mtime_nsec: 0, |
| st_ctime: 0, |
| st_ctime_nsec: 0, |
| st_size: 0, |
| st_blocks: 0, |
| st_blksize: 0, |
| st_flags: 0, |
| st_gen: 0, |
| st_lspare: 0, |
| st_birthtime: 0, |
| st_birthtime_nsec: 0, |
| __unused: [0, 0], |
| } |
| } |
| } |
| } |
| |
| #[cfg(target_os = "macos")] |
| mod stat { |
| pub mod arch { |
| use libc; |
| |
| pub fn default_stat() -> libc::stat { |
| libc::stat { |
| st_dev: 0, |
| st_mode: 0, |
| st_nlink: 0, |
| st_ino: 0, |
| st_uid: 0, |
| st_gid: 0, |
| st_rdev: 0, |
| st_atime: 0, |
| st_atime_nsec: 0, |
| st_mtime: 0, |
| st_mtime_nsec: 0, |
| st_ctime: 0, |
| st_ctime_nsec: 0, |
| st_birthtime: 0, |
| st_birthtime_nsec: 0, |
| st_size: 0, |
| st_blocks: 0, |
| st_blksize: 0, |
| st_flags: 0, |
| st_gen: 0, |
| st_lspare: 0, |
| st_qspare: [0, 0], |
| } |
| } |
| } |
| } |
| |
| #[cfg(target_os = "win32")] |
| mod stat { |
| pub mod arch { |
| use libc; |
| pub fn default_stat() -> libc::stat { |
| libc::stat { |
| st_dev: 0, |
| st_ino: 0, |
| st_mode: 0, |
| st_nlink: 0, |
| st_uid: 0, |
| st_gid: 0, |
| st_rdev: 0, |
| st_size: 0, |
| st_atime: 0, |
| st_mtime: 0, |
| st_ctime: 0, |
| } |
| } |
| } |
| } |
| |
| #[cfg(target_os = "win32")] |
| impl WindowsPath { |
| pub fn stat(&self) -> Option<libc::stat> { |
| #[fixed_stack_segment]; #[inline(never)]; |
| do self.with_c_str |buf| { |
| let mut st = stat::arch::default_stat(); |
| match unsafe { libc::stat(buf, &mut st) } { |
| 0 => Some(st), |
| _ => None, |
| } |
| } |
| } |
| |
| pub fn exists(&self) -> bool { |
| match self.stat() { |
| None => false, |
| Some(_) => true, |
| } |
| } |
| |
| pub fn get_size(&self) -> Option<i64> { |
| match self.stat() { |
| None => None, |
| Some(ref st) => Some(st.st_size as i64), |
| } |
| } |
| |
| pub fn get_mode(&self) -> Option<uint> { |
| match self.stat() { |
| None => None, |
| Some(ref st) => Some(st.st_mode as uint), |
| } |
| } |
| } |
| |
| #[cfg(not(target_os = "win32"))] |
| impl PosixPath { |
| pub fn stat(&self) -> Option<libc::stat> { |
| #[fixed_stack_segment]; #[inline(never)]; |
| do self.with_c_str |buf| { |
| let mut st = stat::arch::default_stat(); |
| match unsafe { libc::stat(buf as *libc::c_char, &mut st) } { |
| 0 => Some(st), |
| _ => None, |
| } |
| } |
| } |
| |
| pub fn exists(&self) -> bool { |
| match self.stat() { |
| None => false, |
| Some(_) => true, |
| } |
| } |
| |
| pub fn get_size(&self) -> Option<i64> { |
| match self.stat() { |
| None => None, |
| Some(ref st) => Some(st.st_size as i64), |
| } |
| } |
| |
| pub fn get_mode(&self) -> Option<uint> { |
| match self.stat() { |
| None => None, |
| Some(ref st) => Some(st.st_mode as uint), |
| } |
| } |
| |
| /// Executes a function `f` on `self` as well as on all of its ancestors. |
| pub fn each_parent(&self, f: &fn(&Path)) { |
| if !self.components.is_empty() { |
| f(self); |
| self.pop().each_parent(f); |
| } |
| } |
| |
| } |
| |
| #[cfg(target_os = "freebsd")] |
| #[cfg(target_os = "linux")] |
| #[cfg(target_os = "macos")] |
| impl PosixPath { |
| pub fn get_atime(&self) -> Option<(i64, int)> { |
| match self.stat() { |
| None => None, |
| Some(ref st) => { |
| Some((st.st_atime as i64, |
| st.st_atime_nsec as int)) |
| } |
| } |
| } |
| |
| pub fn get_mtime(&self) -> Option<(i64, int)> { |
| match self.stat() { |
| None => None, |
| Some(ref st) => { |
| Some((st.st_mtime as i64, |
| st.st_mtime_nsec as int)) |
| } |
| } |
| } |
| |
| pub fn get_ctime(&self) -> Option<(i64, int)> { |
| match self.stat() { |
| None => None, |
| Some(ref st) => { |
| Some((st.st_ctime as i64, |
| st.st_ctime_nsec as int)) |
| } |
| } |
| } |
| } |
| |
| #[cfg(unix)] |
| impl PosixPath { |
| pub fn lstat(&self) -> Option<libc::stat> { |
| #[fixed_stack_segment]; #[inline(never)]; |
| do self.with_c_str |buf| { |
| let mut st = stat::arch::default_stat(); |
| match unsafe { libc::lstat(buf, &mut st) } { |
| 0 => Some(st), |
| _ => None, |
| } |
| } |
| } |
| } |
| |
| #[cfg(target_os = "freebsd")] |
| #[cfg(target_os = "macos")] |
| impl PosixPath { |
| pub fn get_birthtime(&self) -> Option<(i64, int)> { |
| match self.stat() { |
| None => None, |
| Some(ref st) => { |
| Some((st.st_birthtime as i64, |
| st.st_birthtime_nsec as int)) |
| } |
| } |
| } |
| } |
| |
| #[cfg(target_os = "win32")] |
| impl WindowsPath { |
| pub fn get_atime(&self) -> Option<(i64, int)> { |
| match self.stat() { |
| None => None, |
| Some(ref st) => { |
| Some((st.st_atime as i64, 0)) |
| } |
| } |
| } |
| |
| pub fn get_mtime(&self) -> Option<(i64, int)> { |
| match self.stat() { |
| None => None, |
| Some(ref st) => { |
| Some((st.st_mtime as i64, 0)) |
| } |
| } |
| } |
| |
| pub fn get_ctime(&self) -> Option<(i64, int)> { |
| match self.stat() { |
| None => None, |
| Some(ref st) => { |
| Some((st.st_ctime as i64, 0)) |
| } |
| } |
| } |
| |
| /// Executes a function `f` on `self` as well as on all of its ancestors. |
| pub fn each_parent(&self, f: &fn(&Path)) { |
| if !self.components.is_empty() { |
| f(self); |
| self.pop().each_parent(f); |
| } |
| } |
| } |
| |
| impl ToStr for PosixPath { |
| fn to_str(&self) -> ~str { |
| let mut s = ~""; |
| if self.is_absolute { |
| s.push_str("/"); |
| } |
| s + self.components.connect("/") |
| } |
| } |
| |
| impl ToCStr for PosixPath { |
| fn to_c_str(&self) -> c_str::CString { |
| self.to_str().to_c_str() |
| } |
| |
| unsafe fn to_c_str_unchecked(&self) -> c_str::CString { |
| self.to_str().to_c_str_unchecked() |
| } |
| } |
| |
| impl GenericPath for PosixPath { |
| fn from_str(s: &str) -> PosixPath { |
| let components = s.split_iter('/') |
| .filter_map(|s| if s.is_empty() {None} else {Some(s.to_owned())}) |
| .collect(); |
| let is_absolute = (s.len() != 0 && s[0] == '/' as u8); |
| PosixPath { |
| is_absolute: is_absolute, |
| components: components, |
| } |
| } |
| |
| fn with_dirname(&self, d: &str) -> PosixPath { |
| let dpath = PosixPath(d); |
| match self.filename() { |
| Some(ref f) => dpath.push(*f), |
| None => dpath, |
| } |
| } |
| |
| fn with_filename(&self, f: &str) -> PosixPath { |
| assert!(!f.iter().all(posix::is_sep)); |
| self.dir_path().push(f) |
| } |
| |
| fn file_path(&self) -> PosixPath { |
| let cs = match self.filename() { |
| None => ~[], |
| Some(ref f) => ~[(*f).to_owned()] |
| }; |
| PosixPath { |
| is_absolute: false, |
| components: cs, |
| } |
| } |
| |
| fn push(&self, s: &str) -> PosixPath { |
| let mut v = self.components.clone(); |
| for s in s.split_iter(posix::is_sep) { |
| if !s.is_empty() { |
| v.push(s.to_owned()) |
| } |
| } |
| PosixPath { |
| components: v, |
| ..(*self).clone() |
| } |
| } |
| |
| fn push_many<S: Str>(&self, cs: &[S]) -> PosixPath { |
| let mut v = self.components.clone(); |
| for e in cs.iter() { |
| for s in e.as_slice().split_iter(posix::is_sep) { |
| if !s.is_empty() { |
| v.push(s.to_owned()) |
| } |
| } |
| } |
| PosixPath { |
| is_absolute: self.is_absolute, |
| components: v, |
| } |
| } |
| |
| fn pop(&self) -> PosixPath { |
| let mut cs = self.components.clone(); |
| if cs.len() != 0 { |
| cs.pop(); |
| } |
| PosixPath { |
| is_absolute: self.is_absolute, |
| components: cs, |
| } //..self } |
| } |
| |
| fn unsafe_join(&self, other: &PosixPath) -> PosixPath { |
| if other.is_absolute { |
| PosixPath { |
| is_absolute: true, |
| components: other.components.clone(), |
| } |
| } else { |
| self.push_rel(other) |
| } |
| } |
| |
| fn is_restricted(&self) -> bool { |
| false |
| } |
| |
| fn normalize(&self) -> PosixPath { |
| PosixPath { |
| is_absolute: self.is_absolute, |
| components: normalize(self.components), |
| } // ..self } |
| } |
| |
| fn is_absolute(&self) -> bool { |
| self.is_absolute |
| } |
| |
| fn components<'a>(&'a self) -> &'a [~str] { self.components.as_slice() } |
| |
| } |
| |
| |
| impl ToStr for WindowsPath { |
| fn to_str(&self) -> ~str { |
| let mut s = ~""; |
| match self.host { |
| Some(ref h) => { |
| s.push_str("\\\\"); |
| s.push_str(*h); |
| } |
| None => { } |
| } |
| match self.device { |
| Some(ref d) => { |
| s.push_str(*d); |
| s.push_str(":"); |
| } |
| None => { } |
| } |
| if self.is_absolute { |
| s.push_str("\\"); |
| } |
| s + self.components.connect("\\") |
| } |
| } |
| |
| impl c_str::ToCStr for WindowsPath { |
| fn to_c_str(&self) -> c_str::CString { |
| self.to_str().to_c_str() |
| } |
| |
| unsafe fn to_c_str_unchecked(&self) -> c_str::CString { |
| self.to_str().to_c_str_unchecked() |
| } |
| } |
| |
| impl GenericPath for WindowsPath { |
| fn from_str(s: &str) -> WindowsPath { |
| let host; |
| let device; |
| let rest; |
| |
| match ( |
| windows::extract_drive_prefix(s), |
| windows::extract_unc_prefix(s), |
| ) { |
| (Some((ref d, ref r)), _) => { |
| host = None; |
| device = Some((*d).clone()); |
| rest = (*r).clone(); |
| } |
| (None, Some((ref h, ref r))) => { |
| host = Some((*h).clone()); |
| device = None; |
| rest = (*r).clone(); |
| } |
| (None, None) => { |
| host = None; |
| device = None; |
| rest = s.to_owned(); |
| } |
| } |
| |
| let components = rest.split_iter(windows::is_sep) |
| .filter_map(|s| if s.is_empty() {None} else {Some(s.to_owned())}) |
| .collect(); |
| |
| let is_absolute = (rest.len() != 0 && windows::is_sep(rest[0] as char)); |
| WindowsPath { |
| host: host, |
| device: device, |
| is_absolute: is_absolute, |
| components: components, |
| } |
| } |
| |
| fn with_dirname(&self, d: &str) -> WindowsPath { |
| let dpath = WindowsPath(d); |
| match self.filename() { |
| Some(ref f) => dpath.push(*f), |
| None => dpath, |
| } |
| } |
| |
| fn with_filename(&self, f: &str) -> WindowsPath { |
| assert!(! f.iter().all(windows::is_sep)); |
| self.dir_path().push(f) |
| } |
| |
| fn file_path(&self) -> WindowsPath { |
| WindowsPath { |
| host: None, |
| device: None, |
| is_absolute: false, |
| components: match self.filename() { |
| None => ~[], |
| Some(ref f) => ~[(*f).to_owned()], |
| } |
| } |
| } |
| |
| fn push(&self, s: &str) -> WindowsPath { |
| let mut v = self.components.clone(); |
| for s in s.split_iter(windows::is_sep) { |
| if !s.is_empty() { |
| v.push(s.to_owned()) |
| } |
| } |
| WindowsPath { components: v, ..(*self).clone() } |
| } |
| |
| fn push_many<S: Str>(&self, cs: &[S]) -> WindowsPath { |
| let mut v = self.components.clone(); |
| for e in cs.iter() { |
| for s in e.as_slice().split_iter(windows::is_sep) { |
| if !s.is_empty() { |
| v.push(s.to_owned()) |
| } |
| } |
| } |
| // tedious, but as-is, we can't use ..self |
| WindowsPath { |
| host: self.host.clone(), |
| device: self.device.clone(), |
| is_absolute: self.is_absolute, |
| components: v |
| } |
| } |
| |
| fn pop(&self) -> WindowsPath { |
| let mut cs = self.components.clone(); |
| if cs.len() != 0 { |
| cs.pop(); |
| } |
| WindowsPath { |
| host: self.host.clone(), |
| device: self.device.clone(), |
| is_absolute: self.is_absolute, |
| components: cs, |
| } |
| } |
| |
| fn unsafe_join(&self, other: &WindowsPath) -> WindowsPath { |
| /* rhs not absolute is simple push */ |
| if !other.is_absolute { |
| return self.push_many(other.components); |
| } |
| |
| /* if rhs has a host set, then the whole thing wins */ |
| match other.host { |
| Some(ref host) => { |
| return WindowsPath { |
| host: Some((*host).clone()), |
| device: other.device.clone(), |
| is_absolute: true, |
| components: other.components.clone(), |
| }; |
| } |
| _ => {} |
| } |
| |
| /* if rhs has a device set, then a part wins */ |
| match other.device { |
| Some(ref device) => { |
| return WindowsPath { |
| host: None, |
| device: Some((*device).clone()), |
| is_absolute: true, |
| components: other.components.clone(), |
| }; |
| } |
| _ => {} |
| } |
| |
| /* fallback: host and device of lhs win, but the |
| whole path of the right */ |
| WindowsPath { |
| host: self.host.clone(), |
| device: self.device.clone(), |
| is_absolute: self.is_absolute || other.is_absolute, |
| components: other.components.clone(), |
| } |
| } |
| |
| fn is_restricted(&self) -> bool { |
| match self.filestem() { |
| Some(stem) => { |
| // FIXME: #4318 Instead of to_ascii and to_str_ascii, could use |
| // to_ascii_move and to_str_move to not do a unnecessary copy. |
| match stem.to_ascii().to_lower().to_str_ascii() { |
| ~"con" | ~"aux" | ~"com1" | ~"com2" | ~"com3" | ~"com4" | |
| ~"lpt1" | ~"lpt2" | ~"lpt3" | ~"prn" | ~"nul" => true, |
| _ => false |
| } |
| }, |
| None => false |
| } |
| } |
| |
| fn normalize(&self) -> WindowsPath { |
| WindowsPath { |
| host: self.host.clone(), |
| device: match self.device { |
| None => None, |
| |
| // FIXME: #4318 Instead of to_ascii and to_str_ascii, could use |
| // to_ascii_move and to_str_move to not do a unnecessary copy. |
| Some(ref device) => Some(device.to_ascii().to_upper().to_str_ascii()) |
| }, |
| is_absolute: self.is_absolute, |
| components: normalize(self.components) |
| } |
| } |
| |
| fn is_absolute(&self) -> bool { |
| self.is_absolute |
| } |
| |
| fn components<'a>(&'a self) -> &'a [~str] { self.components.as_slice() } |
| |
| } |
| |
| pub fn normalize(components: &[~str]) -> ~[~str] { |
| let mut cs = ~[]; |
| for c in components.iter() { |
| if *c == ~"." && components.len() > 1 { loop; } |
| if *c == ~"" { loop; } |
| if *c == ~".." && cs.len() != 0 { |
| cs.pop(); |
| loop; |
| } |
| cs.push((*c).clone()); |
| } |
| cs |
| } |
| |
| // Various posix helpers. |
| pub mod posix { |
| |
| #[inline] |
| pub fn is_sep(u: char) -> bool { |
| u == '/' |
| } |
| |
| } |
| |
| // Various windows helpers. |
| pub mod windows { |
| use libc; |
| use option::{None, Option, Some}; |
| |
| #[inline] |
| pub fn is_sep(u: char) -> bool { |
| u == '/' || u == '\\' |
| } |
| |
| pub fn extract_unc_prefix(s: &str) -> Option<(~str,~str)> { |
| if (s.len() > 1 && |
| (s[0] == '\\' as u8 || s[0] == '/' as u8) && |
| s[0] == s[1]) { |
| let mut i = 2; |
| while i < s.len() { |
| if is_sep(s[i] as char) { |
| let pre = s.slice(2, i).to_owned(); |
| let rest = s.slice(i, s.len()).to_owned(); |
| return Some((pre, rest)); |
| } |
| i += 1; |
| } |
| } |
| None |
| } |
| |
| pub fn extract_drive_prefix(s: &str) -> Option<(~str,~str)> { |
| #[fixed_stack_segment]; #[inline(never)]; |
| |
| unsafe { |
| if (s.len() > 1 && |
| libc::isalpha(s[0] as libc::c_int) != 0 && |
| s[1] == ':' as u8) { |
| let rest = if s.len() == 2 { |
| ~"" |
| } else { |
| s.slice(2, s.len()).to_owned() |
| }; |
| return Some((s.slice(0,1).to_owned(), rest)); |
| } |
| None |
| } |
| } |
| } |
| |
| #[cfg(test)] |
| mod tests { |
| use option::{None, Some}; |
| use path::{PosixPath, WindowsPath, windows}; |
| |
| #[test] |
| fn test_double_slash_collapsing() { |
| let path = PosixPath("tmp/"); |
| let path = path.push("/hmm"); |
| let path = path.normalize(); |
| assert_eq!(~"tmp/hmm", path.to_str()); |
| |
| let path = WindowsPath("tmp/"); |
| let path = path.push("/hmm"); |
| let path = path.normalize(); |
| assert_eq!(~"tmp\\hmm", path.to_str()); |
| } |
| |
| #[test] |
| fn test_filetype_foo_bar() { |
| let wp = PosixPath("foo.bar"); |
| assert_eq!(wp.filetype(), Some(".bar")); |
| |
| let wp = WindowsPath("foo.bar"); |
| assert_eq!(wp.filetype(), Some(".bar")); |
| } |
| |
| #[test] |
| fn test_filetype_foo() { |
| let wp = PosixPath("foo"); |
| assert_eq!(wp.filetype(), None); |
| |
| let wp = WindowsPath("foo"); |
| assert_eq!(wp.filetype(), None); |
| } |
| |
| #[test] |
| fn test_posix_paths() { |
| fn t(wp: &PosixPath, s: &str) { |
| let ss = wp.to_str(); |
| let sss = s.to_owned(); |
| if (ss != sss) { |
| debug!("got %s", ss); |
| debug!("expected %s", sss); |
| assert_eq!(ss, sss); |
| } |
| } |
| |
| t(&(PosixPath("hi")), "hi"); |
| t(&(PosixPath("/lib")), "/lib"); |
| t(&(PosixPath("hi/there")), "hi/there"); |
| t(&(PosixPath("hi/there.txt")), "hi/there.txt"); |
| |
| t(&(PosixPath("hi/there.txt")), "hi/there.txt"); |
| t(&(PosixPath("hi/there.txt") |
| .with_filetype("")), "hi/there"); |
| |
| t(&(PosixPath("/a/b/c/there.txt") |
| .with_dirname("hi")), "hi/there.txt"); |
| |
| t(&(PosixPath("hi/there.txt") |
| .with_dirname(".")), "./there.txt"); |
| |
| t(&(PosixPath("a/b/c") |
| .push("..")), "a/b/c/.."); |
| |
| t(&(PosixPath("there.txt") |
| .with_filetype("o")), "there.o"); |
| |
| t(&(PosixPath("hi/there.txt") |
| .with_filetype("o")), "hi/there.o"); |
| |
| t(&(PosixPath("hi/there.txt") |
| .with_filetype("o") |
| .with_dirname("/usr/lib")), |
| "/usr/lib/there.o"); |
| |
| t(&(PosixPath("hi/there.txt") |
| .with_filetype("o") |
| .with_dirname("/usr/lib/")), |
| "/usr/lib/there.o"); |
| |
| t(&(PosixPath("hi/there.txt") |
| .with_filetype("o") |
| .with_dirname("/usr//lib//")), |
| "/usr/lib/there.o"); |
| |
| t(&(PosixPath("/usr/bin/rust") |
| .push_many([~"lib", ~"thingy.so"]) |
| .with_filestem("librustc")), |
| "/usr/bin/rust/lib/librustc.so"); |
| |
| } |
| |
| #[test] |
| fn test_posix_push_with_backslash() { |
| let a = PosixPath("/aaa/bbb"); |
| let b = a.push("x\\y"); // \ is not a file separator for posix paths |
| assert_eq!(a.components.len(), 2); |
| assert_eq!(b.components.len(), 3); |
| } |
| |
| #[test] |
| fn test_normalize() { |
| fn t(wp: &PosixPath, s: &str) { |
| let ss = wp.to_str(); |
| let sss = s.to_owned(); |
| if (ss != sss) { |
| debug!("got %s", ss); |
| debug!("expected %s", sss); |
| assert_eq!(ss, sss); |
| } |
| } |
| |
| t(&(PosixPath("hi/there.txt") |
| .with_dirname(".").normalize()), "there.txt"); |
| |
| t(&(PosixPath("a/b/../c/././/../foo.txt/").normalize()), |
| "a/foo.txt"); |
| |
| t(&(PosixPath("a/b/c") |
| .push("..").normalize()), "a/b"); |
| } |
| |
| #[test] |
| fn test_extract_unc_prefixes() { |
| assert!(windows::extract_unc_prefix("\\\\").is_none()); |
| assert!(windows::extract_unc_prefix("//").is_none()); |
| assert!(windows::extract_unc_prefix("\\\\hi").is_none()); |
| assert!(windows::extract_unc_prefix("//hi").is_none()); |
| assert!(windows::extract_unc_prefix("\\\\hi\\") == |
| Some((~"hi", ~"\\"))); |
| assert!(windows::extract_unc_prefix("//hi\\") == |
| Some((~"hi", ~"\\"))); |
| assert!(windows::extract_unc_prefix("\\\\hi\\there") == |
| Some((~"hi", ~"\\there"))); |
| assert!(windows::extract_unc_prefix("//hi/there") == |
| Some((~"hi", ~"/there"))); |
| assert!(windows::extract_unc_prefix( |
| "\\\\hi\\there\\friends.txt") == |
| Some((~"hi", ~"\\there\\friends.txt"))); |
| assert!(windows::extract_unc_prefix( |
| "//hi\\there\\friends.txt") == |
| Some((~"hi", ~"\\there\\friends.txt"))); |
| } |
| |
| #[test] |
| fn test_extract_drive_prefixes() { |
| assert!(windows::extract_drive_prefix("c").is_none()); |
| assert!(windows::extract_drive_prefix("c:") == |
| Some((~"c", ~""))); |
| assert!(windows::extract_drive_prefix("d:") == |
| Some((~"d", ~""))); |
| assert!(windows::extract_drive_prefix("z:") == |
| Some((~"z", ~""))); |
| assert!(windows::extract_drive_prefix("c:\\hi") == |
| Some((~"c", ~"\\hi"))); |
| assert!(windows::extract_drive_prefix("d:hi") == |
| Some((~"d", ~"hi"))); |
| assert!(windows::extract_drive_prefix("c:hi\\there.txt") == |
| Some((~"c", ~"hi\\there.txt"))); |
| assert!(windows::extract_drive_prefix("c:\\hi\\there.txt") == |
| Some((~"c", ~"\\hi\\there.txt"))); |
| } |
| |
| #[test] |
| fn test_windows_paths() { |
| fn t(wp: &WindowsPath, s: &str) { |
| let ss = wp.to_str(); |
| let sss = s.to_owned(); |
| if (ss != sss) { |
| debug!("got %s", ss); |
| debug!("expected %s", sss); |
| assert_eq!(ss, sss); |
| } |
| } |
| |
| t(&(WindowsPath("hi")), "hi"); |
| t(&(WindowsPath("hi/there")), "hi\\there"); |
| t(&(WindowsPath("hi/there.txt")), "hi\\there.txt"); |
| |
| t(&(WindowsPath("there.txt") |
| .with_filetype("o")), "there.o"); |
| |
| t(&(WindowsPath("hi/there.txt") |
| .with_filetype("o")), "hi\\there.o"); |
| |
| t(&(WindowsPath("hi/there.txt") |
| .with_filetype("o") |
| .with_dirname("c:\\program files A")), |
| "c:\\program files A\\there.o"); |
| |
| t(&(WindowsPath("hi/there.txt") |
| .with_filetype("o") |
| .with_dirname("c:\\program files B\\")), |
| "c:\\program files B\\there.o"); |
| |
| t(&(WindowsPath("hi/there.txt") |
| .with_filetype("o") |
| .with_dirname("c:\\program files C\\/")), |
| "c:\\program files C\\there.o"); |
| |
| t(&(WindowsPath("c:\\program files (x86)\\rust") |
| .push_many([~"lib", ~"thingy.dll"]) |
| .with_filename("librustc.dll")), |
| "c:\\program files (x86)\\rust\\lib\\librustc.dll"); |
| |
| t(&(WindowsPath("\\\\computer\\share") |
| .unsafe_join(&WindowsPath("\\a"))), |
| "\\\\computer\\a"); |
| |
| t(&(WindowsPath("//computer/share") |
| .unsafe_join(&WindowsPath("\\a"))), |
| "\\\\computer\\a"); |
| |
| t(&(WindowsPath("//computer/share") |
| .unsafe_join(&WindowsPath("\\\\computer\\share"))), |
| "\\\\computer\\share"); |
| |
| t(&(WindowsPath("C:/whatever") |
| .unsafe_join(&WindowsPath("//computer/share/a/b"))), |
| "\\\\computer\\share\\a\\b"); |
| |
| t(&(WindowsPath("C:") |
| .unsafe_join(&WindowsPath("D:/foo"))), |
| "D:\\foo"); |
| |
| t(&(WindowsPath("C:") |
| .unsafe_join(&WindowsPath("B"))), |
| "C:B"); |
| |
| t(&(WindowsPath("C:") |
| .unsafe_join(&WindowsPath("/foo"))), |
| "C:\\foo"); |
| |
| t(&(WindowsPath("C:\\") |
| .unsafe_join(&WindowsPath("\\bar"))), |
| "C:\\bar"); |
| |
| t(&(WindowsPath("") |
| .unsafe_join(&WindowsPath(""))), |
| ""); |
| |
| t(&(WindowsPath("") |
| .unsafe_join(&WindowsPath("a"))), |
| "a"); |
| |
| t(&(WindowsPath("") |
| .unsafe_join(&WindowsPath("C:\\a"))), |
| "C:\\a"); |
| |
| t(&(WindowsPath("c:\\foo") |
| .normalize()), |
| "C:\\foo"); |
| } |
| |
| #[test] |
| fn test_windows_path_restrictions() { |
| assert_eq!(WindowsPath("hi").is_restricted(), false); |
| assert_eq!(WindowsPath("C:\\NUL").is_restricted(), true); |
| assert_eq!(WindowsPath("C:\\COM1.TXT").is_restricted(), true); |
| assert_eq!(WindowsPath("c:\\prn.exe").is_restricted(), true); |
| } |
| |
| #[test] |
| fn test_is_ancestor_of() { |
| assert!(&PosixPath("/a/b").is_ancestor_of(&PosixPath("/a/b/c/d"))); |
| assert!(!&PosixPath("/a/b/c/d").is_ancestor_of(&PosixPath("/a/b"))); |
| assert!(!&PosixPath("/a/b").is_ancestor_of(&PosixPath("/c/d"))); |
| assert!(&PosixPath("/a/b").is_ancestor_of(&PosixPath("/a/b/c/d"))); |
| assert!(&PosixPath("/").is_ancestor_of(&PosixPath("/a/b/c"))); |
| assert!(!&PosixPath("/").is_ancestor_of(&PosixPath(""))); |
| assert!(!&PosixPath("/a/b/c").is_ancestor_of(&PosixPath(""))); |
| assert!(!&PosixPath("").is_ancestor_of(&PosixPath("/a/b/c"))); |
| |
| assert!(&WindowsPath("C:\\a\\b").is_ancestor_of(&WindowsPath("C:\\a\\b\\c\\d"))); |
| assert!(!&WindowsPath("C:\\a\\b\\c\\d").is_ancestor_of(&WindowsPath("C:\\a\\b"))); |
| assert!(!&WindowsPath("C:\\a\\b").is_ancestor_of(&WindowsPath("C:\\c\\d"))); |
| assert!(&WindowsPath("C:\\a\\b").is_ancestor_of(&WindowsPath("C:\\a\\b\\c\\d"))); |
| assert!(&WindowsPath("C:\\").is_ancestor_of(&WindowsPath("C:\\a\\b\\c"))); |
| assert!(!&WindowsPath("C:\\").is_ancestor_of(&WindowsPath(""))); |
| assert!(!&WindowsPath("C:\\a\\b\\c").is_ancestor_of(&WindowsPath(""))); |
| assert!(!&WindowsPath("").is_ancestor_of(&WindowsPath("C:\\a\\b\\c"))); |
| |
| } |
| |
| #[test] |
| fn test_relative_to1() { |
| let p1 = PosixPath("/usr/bin/rustc"); |
| let p2 = PosixPath("/usr/lib/mylib"); |
| let res = p1.get_relative_to(&p2); |
| assert_eq!(res, PosixPath("../lib")); |
| |
| let p1 = WindowsPath("C:\\usr\\bin\\rustc"); |
| let p2 = WindowsPath("C:\\usr\\lib\\mylib"); |
| let res = p1.get_relative_to(&p2); |
| assert_eq!(res, WindowsPath("..\\lib")); |
| |
| } |
| |
| #[test] |
| fn test_relative_to2() { |
| let p1 = PosixPath("/usr/bin/rustc"); |
| let p2 = PosixPath("/usr/bin/../lib/mylib"); |
| let res = p1.get_relative_to(&p2); |
| assert_eq!(res, PosixPath("../lib")); |
| |
| let p1 = WindowsPath("C:\\usr\\bin\\rustc"); |
| let p2 = WindowsPath("C:\\usr\\bin\\..\\lib\\mylib"); |
| let res = p1.get_relative_to(&p2); |
| assert_eq!(res, WindowsPath("..\\lib")); |
| } |
| |
| #[test] |
| fn test_relative_to3() { |
| let p1 = PosixPath("/usr/bin/whatever/rustc"); |
| let p2 = PosixPath("/usr/lib/whatever/mylib"); |
| let res = p1.get_relative_to(&p2); |
| assert_eq!(res, PosixPath("../../lib/whatever")); |
| |
| let p1 = WindowsPath("C:\\usr\\bin\\whatever\\rustc"); |
| let p2 = WindowsPath("C:\\usr\\lib\\whatever\\mylib"); |
| let res = p1.get_relative_to(&p2); |
| assert_eq!(res, WindowsPath("..\\..\\lib\\whatever")); |
| |
| } |
| |
| #[test] |
| fn test_relative_to4() { |
| let p1 = PosixPath("/usr/bin/whatever/../rustc"); |
| let p2 = PosixPath("/usr/lib/whatever/mylib"); |
| let res = p1.get_relative_to(&p2); |
| assert_eq!(res, PosixPath("../lib/whatever")); |
| |
| let p1 = WindowsPath("C:\\usr\\bin\\whatever\\..\\rustc"); |
| let p2 = WindowsPath("C:\\usr\\lib\\whatever\\mylib"); |
| let res = p1.get_relative_to(&p2); |
| assert_eq!(res, WindowsPath("..\\lib\\whatever")); |
| |
| } |
| |
| #[test] |
| fn test_relative_to5() { |
| let p1 = PosixPath("/usr/bin/whatever/../rustc"); |
| let p2 = PosixPath("/usr/lib/whatever/../mylib"); |
| let res = p1.get_relative_to(&p2); |
| assert_eq!(res, PosixPath("../lib")); |
| |
| let p1 = WindowsPath("C:\\usr\\bin/whatever\\..\\rustc"); |
| let p2 = WindowsPath("C:\\usr\\lib\\whatever\\..\\mylib"); |
| let res = p1.get_relative_to(&p2); |
| assert_eq!(res, WindowsPath("..\\lib")); |
| } |
| |
| #[test] |
| fn test_relative_to6() { |
| let p1 = PosixPath("/1"); |
| let p2 = PosixPath("/2/3"); |
| let res = p1.get_relative_to(&p2); |
| assert_eq!(res, PosixPath("2")); |
| |
| let p1 = WindowsPath("C:\\1"); |
| let p2 = WindowsPath("C:\\2\\3"); |
| let res = p1.get_relative_to(&p2); |
| assert_eq!(res, WindowsPath("2")); |
| |
| } |
| |
| #[test] |
| fn test_relative_to7() { |
| let p1 = PosixPath("/1/2"); |
| let p2 = PosixPath("/3"); |
| let res = p1.get_relative_to(&p2); |
| assert_eq!(res, PosixPath("..")); |
| |
| let p1 = WindowsPath("C:\\1\\2"); |
| let p2 = WindowsPath("C:\\3"); |
| let res = p1.get_relative_to(&p2); |
| assert_eq!(res, WindowsPath("..")); |
| |
| } |
| |
| #[test] |
| fn test_relative_to8() { |
| let p1 = PosixPath("/home/brian/Dev/rust/build/").push_rel( |
| &PosixPath("stage2/lib/rustc/i686-unknown-linux-gnu/lib/librustc.so")); |
| let p2 = PosixPath("/home/brian/Dev/rust/build/stage2/bin/..").push_rel( |
| &PosixPath("lib/rustc/i686-unknown-linux-gnu/lib/libstd.so")); |
| let res = p1.get_relative_to(&p2); |
| debug!("test_relative_to8: %s vs. %s", |
| res.to_str(), |
| PosixPath(".").to_str()); |
| assert_eq!(res, PosixPath(".")); |
| |
| let p1 = WindowsPath("C:\\home\\brian\\Dev\\rust\\build\\").push_rel( |
| &WindowsPath("stage2\\lib\\rustc\\i686-unknown-linux-gnu\\lib\\librustc.so")); |
| let p2 = WindowsPath("\\home\\brian\\Dev\\rust\\build\\stage2\\bin\\..").push_rel( |
| &WindowsPath("lib\\rustc\\i686-unknown-linux-gnu\\lib\\libstd.so")); |
| let res = p1.get_relative_to(&p2); |
| debug!("test_relative_to8: %s vs. %s", |
| res.to_str(), |
| WindowsPath(".").to_str()); |
| assert_eq!(res, WindowsPath(".")); |
| |
| } |
| |
| |
| #[test] |
| fn test_is_parent_of() { |
| fn is_parent_of_pp(p: &PosixPath, q: &PosixPath) -> bool { |
| p.is_parent_of(q) |
| } |
| |
| assert!(is_parent_of_pp(&PosixPath("/a/b/c/d/e"), &PosixPath("c/d/e"))); |
| assert!(!is_parent_of_pp(&PosixPath("a/b/c/d/e"), &PosixPath("c/d/e"))); |
| assert!(!is_parent_of_pp(&PosixPath("/a/b/c/d/e"), &PosixPath("/c/d/e"))); |
| assert!(!is_parent_of_pp(&PosixPath(""), &PosixPath(""))); |
| assert!(!is_parent_of_pp(&PosixPath(""), &PosixPath("a/b/c"))); |
| assert!(is_parent_of_pp(&PosixPath("/a/b/c"), &PosixPath(""))); |
| assert!(is_parent_of_pp(&PosixPath("/a/b/c"), &PosixPath("a/b/c"))); |
| assert!(!is_parent_of_pp(&PosixPath("/a/b/c"), &PosixPath("d/e/f"))); |
| |
| fn is_parent_of_wp(p: &WindowsPath, q: &WindowsPath) -> bool { |
| p.is_parent_of(q) |
| } |
| |
| let abcde = WindowsPath("C:\\a\\b\\c\\d\\e"); |
| let rel_abcde = WindowsPath("a\\b\\c\\d\\e"); |
| let cde = WindowsPath("c\\d\\e"); |
| let slashcde = WindowsPath("C:\\c\\d\\e"); |
| let empty = WindowsPath(""); |
| let abc = WindowsPath("C:\\a\\b\\c"); |
| let rel_abc = WindowsPath("a\\b\\c"); |
| let def = WindowsPath("d\\e\\f"); |
| |
| assert!(is_parent_of_wp(&abcde, &cde)); |
| assert!(!is_parent_of_wp(&rel_abcde, &cde)); |
| assert!(!is_parent_of_wp(&abcde, &slashcde)); |
| assert!(!is_parent_of_wp(&empty, &empty)); |
| assert!(!is_parent_of_wp(&empty, &rel_abc)); |
| assert!(is_parent_of_wp(&abc, &empty)); |
| assert!(is_parent_of_wp(&abc, &rel_abc)); |
| assert!(!is_parent_of_wp(&abc, &def)); |
| } |
| |
| } |