about summary refs log tree commit diff
path: root/src/libstd/path/mod.rs
diff options
context:
space:
mode:
authorKevin Ballard <kevin@sb.org>2013-10-05 19:49:32 -0700
committerKevin Ballard <kevin@sb.org>2013-10-15 22:18:30 -0700
commitd6d9b926836b1f1c2b8b3fe4ab35dc63bec7ffcd (patch)
treee197783b86700e71d94c9bc6d0254eb25b16cc0c /src/libstd/path/mod.rs
parented539e14712539473c3e89604cb69e2307110772 (diff)
downloadrust-d6d9b926836b1f1c2b8b3fe4ab35dc63bec7ffcd.tar.gz
rust-d6d9b926836b1f1c2b8b3fe4ab35dc63bec7ffcd.zip
path2: Adjust the API to remove all the _str mutation methods
Add a new trait BytesContainer that is implemented for both byte vectors
and strings.

Convert Path::from_vec and ::from_str to one function, Path::new().

Remove all the _str-suffixed mutation methods (push, join, with_*,
set_*) and modify the non-suffixed versions to use BytesContainer.
Diffstat (limited to 'src/libstd/path/mod.rs')
-rw-r--r--src/libstd/path/mod.rs469
1 files changed, 222 insertions, 247 deletions
diff --git a/src/libstd/path/mod.rs b/src/libstd/path/mod.rs
index 1ecb31a2a87..561155f2258 100644
--- a/src/libstd/path/mod.rs
+++ b/src/libstd/path/mod.rs
@@ -20,8 +20,7 @@ appropriate platform-specific path variant.
 Both `PosixPath` and `WindowsPath` implement a trait `GenericPath`, which
 contains the set of methods that behave the same for both paths. They each also
 implement some methods that could not be expressed in `GenericPath`, yet behave
-identically for both path flavors, such as `::from_str()` or
-`.component_iter()`.
+identically for both path flavors, such as `.component_iter()`.
 
 The three main design goals of this module are 1) to avoid unnecessary
 allocation, 2) to behave the same regardless of which flavor of path is being
@@ -35,8 +34,8 @@ 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`.
 
-Creation of a path is typically done with either `Path::from_str(some_str)` or
-`Path::from_vec(some_vec)`. This path can be modified with `.push()` and
+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
 `.pop()` (and other setters). The resulting Path can either be passed to another
 API that expects a path, or can be turned into a &[u8] with `.as_vec()` or a
 Option<&str> with `.as_str()`. Similarly, attributes of the path can be queried
@@ -44,10 +43,10 @@ with methods such as `.filename()`. There are also methods that return a new
 path instead of modifying the receiver, such as `.join()` or `.dir_path()`.
 
 Paths are always kept in normalized form. This means that creating the path
-`Path::from_str("a/b/../c")` will return the path `a/c`. Similarly any attempt
+`Path::new("a/b/../c")` will return the path `a/c`. Similarly any attempt
 to mutate the path will always leave it in normalized form.
 
-When rendering a path to some form of display, there is a method `.display()`
+When rendering a path to some form of output, there is a method `.display()`
 which is compatible with the `format!()` parameter `{}`. This will render the
 path as a string, replacing all non-utf8 sequences with the Replacement
 Character (U+FFFD). As such it is not suitable for passing to any API that
@@ -56,10 +55,10 @@ actually operates on the path; it is only intended for display.
 ## Example
 
 ```rust
-let mut path = Path::from_str("/tmp/path");
+let mut path = Path::new("/tmp/path");
 debug2!("path: {}", path.display());
-path.set_filename_str("foo");
-path.push_str("bar");
+path.set_filename("foo");
+path.push("bar");
 debug2!("new path: {}", path.display());
 let b = std::os::path_exists(&path);
 debug2!("path exists: {}", b);
@@ -70,6 +69,7 @@ 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};
@@ -131,7 +131,7 @@ condition! {
 
 /// A trait that represents the generic operations available on paths
 pub trait GenericPath: Clone + GenericPathUnsafe {
-    /// Creates a new Path from a byte vector.
+    /// Creates a new Path from a byte vector or string.
     /// The resulting Path will always be normalized.
     ///
     /// # Failure
@@ -140,52 +140,24 @@ pub trait GenericPath: Clone + GenericPathUnsafe {
     ///
     /// See individual Path impls for additional restrictions.
     #[inline]
-    fn from_vec(path: &[u8]) -> Self {
-        if contains_nul(path) {
-            let path = self::null_byte::cond.raise(path.to_owned());
+    fn new<T: BytesContainer>(path: T) -> Self {
+        if contains_nul(path.container_as_bytes()) {
+            let path = self::null_byte::cond.raise(path.container_into_owned_bytes());
             assert!(!contains_nul(path));
-            unsafe { GenericPathUnsafe::from_vec_unchecked(path) }
+            unsafe { GenericPathUnsafe::new_unchecked(path) }
         } else {
-            unsafe { GenericPathUnsafe::from_vec_unchecked(path) }
+            unsafe { GenericPathUnsafe::new_unchecked(path) }
         }
     }
 
-    /// Creates a new Path from a byte vector, if possible.
+    /// Creates a new Path from a byte vector or string, if possible.
     /// The resulting Path will always be normalized.
     #[inline]
-    fn from_vec_opt(path: &[u8]) -> Option<Self> {
-        if contains_nul(path) {
+    fn new_opt<T: BytesContainer>(path: T) -> Option<Self> {
+        if contains_nul(path.container_as_bytes()) {
             None
         } else {
-            Some(unsafe { GenericPathUnsafe::from_vec_unchecked(path) })
-        }
-    }
-
-    /// Creates a new Path from a string.
-    /// The resulting Path will always be normalized.
-    ///
-    /// # Failure
-    ///
-    /// Raises the `null_byte` condition if the path contains a NUL.
-    #[inline]
-    fn from_str(path: &str) -> Self {
-        let v = path.as_bytes();
-        if contains_nul(v) {
-            GenericPath::from_vec(path.as_bytes()) // let from_vec handle the condition
-        } else {
-            unsafe { GenericPathUnsafe::from_str_unchecked(path) }
-        }
-    }
-
-    /// Creates a new Path from a string, if possible.
-    /// The resulting Path will always be normalized.
-    #[inline]
-    fn from_str_opt(path: &str) -> Option<Self> {
-        let v = path.as_bytes();
-        if contains_nul(v) {
-            None
-        } else {
-            Some(unsafe { GenericPathUnsafe::from_str_unchecked(path) })
+            Some(unsafe { GenericPathUnsafe::new_unchecked(path) })
         }
     }
 
@@ -199,7 +171,7 @@ pub trait GenericPath: Clone + GenericPathUnsafe {
         let v = path.as_bytes();
         // v is NUL-terminated. Strip it off
         let v = v.slice_to(v.len()-1);
-        unsafe { GenericPathUnsafe::from_vec_unchecked(v) }
+        unsafe { GenericPathUnsafe::new_unchecked(v) }
     }
 
     /// Returns the path as a string, if possible.
@@ -209,9 +181,15 @@ pub trait GenericPath: Clone + GenericPathUnsafe {
         str::from_utf8_slice_opt(self.as_vec())
     }
 
+    /// Converts the Path into an owned string, if possible
+    fn into_str(self) -> Option<~str>;
+
     /// Returns the path as a byte vector
     fn as_vec<'a>(&'a self) -> &'a [u8];
 
+    /// Converts the Path into an owned byte vector
+    fn into_vec(self) -> ~[u8];
+
     /// Provides the path as a string
     ///
     /// If the path is not UTF-8, invalid sequences will be replaced with the unicode
@@ -345,115 +323,91 @@ pub trait GenericPath: Clone + GenericPathUnsafe {
         self.extension().and_then(str::from_utf8_slice_opt)
     }
 
-    /// Replaces the directory portion of the path with the given byte vector.
+    /// Replaces the directory portion of the path with the given byte vector or string.
     /// If `self` represents the root of the filesystem hierarchy, the last path component
-    /// of the given byte vector becomes the filename.
+    /// of the argument becomes the filename.
     ///
     /// # Failure
     ///
     /// Raises the `null_byte` condition if the dirname contains a NUL.
     #[inline]
-    fn set_dirname(&mut self, dirname: &[u8]) {
-        if contains_nul(dirname) {
-            let dirname = self::null_byte::cond.raise(dirname.to_owned());
+    fn set_dirname<T: BytesContainer>(&mut self, dirname: T) {
+        if contains_nul(dirname.container_as_bytes()) {
+            let dirname = self::null_byte::cond.raise(dirname.container_into_owned_bytes());
             assert!(!contains_nul(dirname));
             unsafe { self.set_dirname_unchecked(dirname) }
         } else {
             unsafe { self.set_dirname_unchecked(dirname) }
         }
     }
-    /// Replaces the directory portion of the path with the given string.
-    /// See `set_dirname` for details.
-    #[inline]
-    fn set_dirname_str(&mut self, dirname: &str) {
-        if contains_nul(dirname.as_bytes()) {
-            self.set_dirname(dirname.as_bytes()) // triggers null_byte condition
-        } else {
-            unsafe { self.set_dirname_str_unchecked(dirname) }
-        }
-    }
-    /// Replaces the filename portion of the path with the given byte vector.
+    /// Replaces the filename portion of the path with the given byte vector or string.
     /// If the replacement name is [], this is equivalent to popping the path.
     ///
     /// # Failure
     ///
     /// Raises the `null_byte` condition if the filename contains a NUL.
     #[inline]
-    fn set_filename(&mut self, filename: &[u8]) {
-        if contains_nul(filename) {
-            let filename = self::null_byte::cond.raise(filename.to_owned());
+    fn set_filename<T: BytesContainer>(&mut self, filename: T) {
+        if contains_nul(filename.container_as_bytes()) {
+            let filename = self::null_byte::cond.raise(filename.container_into_owned_bytes());
             assert!(!contains_nul(filename));
             unsafe { self.set_filename_unchecked(filename) }
         } else {
             unsafe { self.set_filename_unchecked(filename) }
         }
     }
-    /// Replaces the filename portion of the path with the given string.
-    /// See `set_filename` for details.
-    #[inline]
-    fn set_filename_str(&mut self, filename: &str) {
-        if contains_nul(filename.as_bytes()) {
-            self.set_filename(filename.as_bytes()) // triggers null_byte condition
-        } else {
-            unsafe { self.set_filename_str_unchecked(filename) }
-        }
-    }
-    /// Replaces the filestem with the given byte vector.
+    /// Replaces the filestem with the given byte vector or string.
     /// If there is no extension in `self` (or `self` has no filename), this is equivalent
-    /// to `set_filename`. Otherwise, if the given byte vector is [], the extension (including
+    /// to `set_filename`. Otherwise, if the argument is [] or "", the extension (including
     /// the preceding '.') becomes the new filename.
     ///
     /// # Failure
     ///
     /// Raises the `null_byte` condition if the filestem contains a NUL.
-    fn set_filestem(&mut self, filestem: &[u8]) {
+    fn set_filestem<T: BytesContainer>(&mut self, filestem: T) {
         // borrowck is being a pain here
         let val = {
             match self.filename() {
-                None => None,
+                None => Left(filestem),
                 Some(name) => {
                     let dot = '.' as u8;
                     match name.rposition_elem(&dot) {
-                        None | Some(0) => None,
+                        None | Some(0) => Left(filestem),
                         Some(idx) => {
                             let mut v;
-                            if contains_nul(filestem) {
-                                let filestem = self::null_byte::cond.raise(filestem.to_owned());
+                            if contains_nul(filestem.container_as_bytes()) {
+                                let filestem = filestem.container_into_owned_bytes();
+                                let filestem = self::null_byte::cond.raise(filestem);
                                 assert!(!contains_nul(filestem));
                                 v = filestem;
                                 let n = v.len();
                                 v.reserve(n + name.len() - idx);
                             } else {
+                                let filestem = filestem.container_as_bytes();
                                 v = vec::with_capacity(filestem.len() + name.len() - idx);
                                 v.push_all(filestem);
                             }
                             v.push_all(name.slice_from(idx));
-                            Some(v)
+                            Right(v)
                         }
                     }
                 }
             }
         };
         match val {
-            None => self.set_filename(filestem),
-            Some(v) => unsafe { self.set_filename_unchecked(v) }
+            Left(v)  => self.set_filename(v),
+            Right(v) => unsafe { self.set_filename_unchecked(v) }
         }
     }
-    /// Replaces the filestem with the given string.
-    /// See `set_filestem` for details.
-    #[inline]
-    fn set_filestem_str(&mut self, filestem: &str) {
-        self.set_filestem(filestem.as_bytes())
-    }
-    /// Replaces the extension with the given byte vector.
+    /// Replaces the extension with the given byte vector or string.
     /// If there is no extension in `self`, this adds one.
-    /// If the given byte vector is [], this removes the extension.
+    /// If the argument is [] or "", this removes the extension.
     /// If `self` has no filename, this is a no-op.
     ///
     /// # Failure
     ///
     /// Raises the `null_byte` condition if the extension contains a NUL.
-    fn set_extension(&mut self, extension: &[u8]) {
+    fn set_extension<T: BytesContainer>(&mut self, extension: T) {
         // borrowck causes problems here too
         let val = {
             match self.filename() {
@@ -462,12 +416,12 @@ pub trait GenericPath: Clone + GenericPathUnsafe {
                     let dot = '.' as u8;
                     match name.rposition_elem(&dot) {
                         None | Some(0) => {
-                            if extension.is_empty() {
+                            if extension.container_as_bytes().is_empty() {
                                 None
                             } else {
                                 let mut v;
-                                if contains_nul(extension) {
-                                    let ext = extension.to_owned();
+                                if contains_nul(extension.container_as_bytes()) {
+                                    let ext = extension.container_into_owned_bytes();
                                     let extension = self::null_byte::cond.raise(ext);
                                     assert!(!contains_nul(extension));
                                     v = vec::with_capacity(name.len() + extension.len() + 1);
@@ -475,6 +429,7 @@ pub trait GenericPath: Clone + GenericPathUnsafe {
                                     v.push(dot);
                                     v.push_all(extension);
                                 } else {
+                                    let extension = extension.container_as_bytes();
                                     v = vec::with_capacity(name.len() + extension.len() + 1);
                                     v.push_all(name);
                                     v.push(dot);
@@ -484,18 +439,19 @@ pub trait GenericPath: Clone + GenericPathUnsafe {
                             }
                         }
                         Some(idx) => {
-                            if extension.is_empty() {
+                            if extension.container_as_bytes().is_empty() {
                                 Some(name.slice_to(idx).to_owned())
                             } else {
                                 let mut v;
-                                if contains_nul(extension) {
-                                    let ext = extension.to_owned();
+                                if contains_nul(extension.container_as_bytes()) {
+                                    let ext = extension.container_into_owned_bytes();
                                     let extension = self::null_byte::cond.raise(ext);
                                     assert!(!contains_nul(extension));
                                     v = vec::with_capacity(idx + extension.len() + 1);
                                     v.push_all(name.slice_to(idx+1));
                                     v.push_all(extension);
                                 } else {
+                                    let extension = extension.container_as_bytes();
                                     v = vec::with_capacity(idx + extension.len() + 1);
                                     v.push_all(name.slice_to(idx+1));
                                     v.push_all(extension);
@@ -512,31 +468,25 @@ pub trait GenericPath: Clone + GenericPathUnsafe {
             Some(v) => unsafe { self.set_filename_unchecked(v) }
         }
     }
-    /// Replaces the extension with the given string.
-    /// See `set_extension` for details.
-    #[inline]
-    fn set_extension_str(&mut self, extension: &str) {
-        self.set_extension(extension.as_bytes())
-    }
-    /// Adds the given extension (as a byte vector) to the file.
+    /// Adds the given extension (as a byte vector or string) to the file.
     /// This does not remove any existing extension.
     /// `foo.bar`.add_extension(`baz`) becomes `foo.bar.baz`.
     /// If `self` has no filename, this is a no-op.
-    /// If the given byte vector is [], this is a no-op.
+    /// If the argument is [] or "", this is a no-op.
     ///
     /// # Failure
     ///
     /// Raises the `null_byte` condition if the extension contains a NUL.
-    fn add_extension(&mut self, extension: &[u8]) {
-        if extension.is_empty() { return; }
+    fn add_extension<T: BytesContainer>(&mut self, extension: T) {
+        if extension.container_as_bytes().is_empty() { return; }
         // appease borrowck
         let val = {
             match self.filename() {
                 None => None,
                 Some(name) => {
                     let mut v;
-                    if contains_nul(extension) {
-                        let ext = extension.to_owned();
+                    if contains_nul(extension.container_as_bytes()) {
+                        let ext = extension.container_into_owned_bytes();
                         let extension = self::null_byte::cond.raise(ext);
                         assert!(!contains_nul(extension));
                         v = vec::with_capacity(name.len() + 1 + extension.len());
@@ -544,6 +494,7 @@ pub trait GenericPath: Clone + GenericPathUnsafe {
                         v.push('.' as u8);
                         v.push_all(extension);
                     } else {
+                        let extension = extension.container_as_bytes();
                         v = vec::with_capacity(name.len() + 1 + extension.len());
                         v.push_all(name);
                         v.push('.' as u8);
@@ -558,105 +509,71 @@ pub trait GenericPath: Clone + GenericPathUnsafe {
             Some(v) => unsafe { self.set_filename_unchecked(v) }
         }
     }
-    /// Adds the given extension (as a string) to the file.
-    /// See `add_extension` for details.
-    #[inline]
-    fn add_extension_str(&mut self, extension: &str) {
-        self.add_extension(extension.as_bytes())
-    }
 
-    /// Returns a new Path constructed by replacing the dirname with the given byte vector.
+    /// Returns a new Path constructed by replacing the dirname with the given
+    /// byte vector or string.
     /// See `set_dirname` for details.
     ///
     /// # Failure
     ///
     /// Raises the `null_byte` condition if the dirname contains a NUL.
     #[inline]
-    fn with_dirname(&self, dirname: &[u8]) -> Self {
+    fn with_dirname<T: BytesContainer>(&self, dirname: T) -> Self {
         let mut p = self.clone();
         p.set_dirname(dirname);
         p
     }
-    /// Returns a new Path constructed by replacing the dirname with the given string.
-    /// See `set_dirname` for details.
-    #[inline]
-    fn with_dirname_str(&self, dirname: &str) -> Self {
-        let mut p = self.clone();
-        p.set_dirname_str(dirname);
-        p
-    }
-    /// Returns a new Path constructed by replacing the filename with the given byte vector.
+    /// Returns a new Path constructed by replacing the filename with the given
+    /// byte vector or string.
     /// See `set_filename` for details.
     ///
     /// # Failure
     ///
     /// Raises the `null_byte` condition if the filename contains a NUL.
     #[inline]
-    fn with_filename(&self, filename: &[u8]) -> Self {
+    fn with_filename<T: BytesContainer>(&self, filename: T) -> Self {
         let mut p = self.clone();
         p.set_filename(filename);
         p
     }
-    /// Returns a new Path constructed by replacing the filename with the given string.
-    /// See `set_filename` for details.
-    #[inline]
-    fn with_filename_str(&self, filename: &str) -> Self {
-        let mut p = self.clone();
-        p.set_filename_str(filename);
-        p
-    }
-    /// Returns a new Path constructed by setting the filestem to the given byte vector.
+    /// Returns a new Path constructed by setting the filestem to the given
+    /// byte vector or string.
     /// See `set_filestem` for details.
     ///
     /// # Failure
     ///
     /// Raises the `null_byte` condition if the filestem contains a NUL.
     #[inline]
-    fn with_filestem(&self, filestem: &[u8]) -> Self {
+    fn with_filestem<T: BytesContainer>(&self, filestem: T) -> Self {
         let mut p = self.clone();
         p.set_filestem(filestem);
         p
     }
-    /// Returns a new Path constructed by setting the filestem to the given string.
-    /// See `set_filestem` for details.
-    #[inline]
-    fn with_filestem_str(&self, filestem: &str) -> Self {
-        let mut p = self.clone();
-        p.set_filestem_str(filestem);
-        p
-    }
-    /// Returns a new Path constructed by setting the extension to the given byte vector.
+    /// Returns a new Path constructed by setting the extension to the given
+    /// byte vector or string.
     /// See `set_extension` for details.
     ///
     /// # Failure
     ///
     /// Raises the `null_byte` condition if the extension contains a NUL.
     #[inline]
-    fn with_extension(&self, extension: &[u8]) -> Self {
+    fn with_extension<T: BytesContainer>(&self, extension: T) -> Self {
         let mut p = self.clone();
         p.set_extension(extension);
         p
     }
-    /// Returns a new Path constructed by setting the extension to the given string.
-    /// See `set_extension` for details.
-    #[inline]
-    fn with_extension_str(&self, extension: &str) -> Self {
-        let mut p = self.clone();
-        p.set_extension_str(extension);
-        p
-    }
 
     /// Returns the directory component of `self`, as a Path.
     /// If `self` represents the root of the filesystem hierarchy, returns `self`.
     fn dir_path(&self) -> Self {
         // self.dirname() returns a NUL-free vector
-        unsafe { GenericPathUnsafe::from_vec_unchecked(self.dirname()) }
+        unsafe { GenericPathUnsafe::new_unchecked(self.dirname()) }
     }
     /// Returns the file component of `self`, as a relative Path.
     /// If `self` represents the root of the filesystem hierarchy, returns None.
     fn file_path(&self) -> Option<Self> {
         // self.filename() returns a NUL-free vector
-        self.filename().map_move(|v| unsafe { GenericPathUnsafe::from_vec_unchecked(v) })
+        self.filename().map_move(|v| unsafe { GenericPathUnsafe::new_unchecked(v) })
     }
 
     /// Returns a Path that represents the filesystem root that `self` is rooted in.
@@ -664,51 +581,41 @@ pub trait GenericPath: Clone + GenericPathUnsafe {
     /// If `self` is not absolute, or vol-relative in the case of Windows, this returns None.
     fn root_path(&self) -> Option<Self>;
 
-    /// Pushes a path (as a byte vector) onto `self`.
+    /// Pushes a path (as a byte vector or string) onto `self`.
     /// If the argument represents an absolute path, it replaces `self`.
     ///
     /// # Failure
     ///
     /// Raises the `null_byte` condition if the path contains a NUL.
     #[inline]
-    fn push(&mut self, path: &[u8]) {
-        if contains_nul(path) {
-            let path = self::null_byte::cond.raise(path.to_owned());
+    fn push<T: BytesContainer>(&mut self, path: T) {
+        if contains_nul(path.container_as_bytes()) {
+            let path = self::null_byte::cond.raise(path.container_into_owned_bytes());
             assert!(!contains_nul(path));
             unsafe { self.push_unchecked(path) }
         } else {
             unsafe { self.push_unchecked(path) }
         }
     }
-    /// Pushes a path (as a string) onto `self.
-    /// See `push` for details.
-    #[inline]
-    fn push_str(&mut self, path: &str) {
-        if contains_nul(path.as_bytes()) {
-            self.push(path.as_bytes()) // triggers null_byte condition
-        } else {
-            unsafe { self.push_str_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) onto `self`.
+    /// Pushes multiple paths (as byte vectors or strings) onto `self`.
     /// See `push` for details.
     #[inline]
-    fn push_many<V: Vector<u8>>(&mut self, paths: &[V]) {
-        for p in paths.iter() {
-            self.push(p.as_slice());
-        }
-    }
-    /// Pushes multiple paths (as strings) onto `self`.
-    #[inline]
-    fn push_many_str<S: Str>(&mut self, paths: &[S]) {
-        for p in paths.iter() {
-            self.push_str(p.as_slice());
+    fn push_many<T: BytesContainer>(&mut self, paths: &[T]) {
+        let t: Option<T> = None;
+        if BytesContainer::is_str(t) {
+            for p in paths.iter() {
+                self.push(p.container_as_str())
+            }
+        } else {
+            for p in paths.iter() {
+                self.push(p.container_as_bytes())
+            }
         }
     }
     /// Pops the last path component off of `self` and returns it.
@@ -722,26 +629,19 @@ pub trait GenericPath: Clone + GenericPathUnsafe {
         self.pop().and_then(|v| str::from_utf8_owned_opt(v))
     }
 
-    /// Returns a new Path constructed by joining `self` with the given path (as a byte vector).
+    /// Returns a new Path constructed by joining `self` with the given path
+    /// (as a byte vector or string).
     /// If the given path is absolute, the new Path will represent just that.
     ///
     /// # Failure
     ///
     /// Raises the `null_byte` condition if the path contains a NUL.
     #[inline]
-    fn join(&self, path: &[u8]) -> Self {
+    fn join<T: BytesContainer>(&self, path: T) -> Self {
         let mut p = self.clone();
         p.push(path);
         p
     }
-    /// Returns a new Path constructed by joining `self` with the given path (as a string).
-    /// See `join` for details.
-    #[inline]
-    fn join_str(&self, path: &str) -> Self {
-        let mut p = self.clone();
-        p.push_str(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]
@@ -750,22 +650,15 @@ pub trait GenericPath: Clone + GenericPathUnsafe {
         p.push_path(path);
         p
     }
-    /// Returns a new Path constructed by joining `self` with the given paths (as byte vectors).
+    /// Returns a new Path constructed by joining `self` with the given paths
+    /// (as byte vectors or strings).
     /// See `join` for details.
     #[inline]
-    fn join_many<V: Vector<u8>>(&self, paths: &[V]) -> Self {
+    fn join_many<T: BytesContainer>(&self, paths: &[T]) -> Self {
         let mut p = self.clone();
         p.push_many(paths);
         p
     }
-    /// Returns a new Path constructed by joining `self` with the given paths (as strings).
-    /// See `join` for details.
-    #[inline]
-    fn join_many_str<S: Str>(&self, paths: &[S]) -> Self {
-        let mut p = self.clone();
-        p.push_many_str(paths);
-        p
-    }
 
     /// Returns whether `self` represents an absolute path.
     /// An absolute path is defined as one that, when joined to another path, will
@@ -805,57 +698,59 @@ pub trait GenericPath: Clone + GenericPathUnsafe {
         }
         true
     }
+
+    /// Returns whether the relative path `child` is a suffix of `self`.
+    fn ends_with_path(&self, child: &Self) -> bool;
+}
+
+/// A trait that represents something bytes-like (e.g. a &[u8] or a &str)
+pub trait BytesContainer {
+    /// Returns a &[u8] representing the receiver
+    fn container_as_bytes<'a>(&'a self) -> &'a [u8];
+    /// Consumes the receiver and converts it into ~[u8]
+    #[inline]
+    fn container_into_owned_bytes(self) -> ~[u8] {
+        self.container_as_bytes().to_owned()
+    }
+    /// Returns the receiver interpreted as a utf-8 string
+    ///
+    /// # Failure
+    ///
+    /// Raises `str::null_byte` if not utf-8
+    #[inline]
+    fn container_as_str<'a>(&'a self) -> &'a str {
+        str::from_utf8_slice(self.container_as_bytes())
+    }
+    /// Returns the receiver interpreted as a utf-8 string, if possible
+    #[inline]
+    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
+    // FIXME (#8888): Remove unused arg once ::<for T> works
+    #[inline]
+    fn is_str(_: Option<Self>) -> bool { false }
 }
 
 /// A trait that represents the unsafe operations on GenericPaths
 pub trait GenericPathUnsafe {
-    /// Creates a new Path from a byte vector without checking for null bytes.
+    /// Creates a new Path without checking for null bytes.
     /// The resulting Path will always be normalized.
-    unsafe fn from_vec_unchecked(path: &[u8]) -> Self;
+    unsafe fn new_unchecked<T: BytesContainer>(path: T) -> Self;
 
-    /// Creates a new Path from a str without checking for null bytes.
-    /// The resulting Path will always be normalized.
-    #[inline]
-    unsafe fn from_str_unchecked(path: &str) -> Self {
-        GenericPathUnsafe::from_vec_unchecked(path.as_bytes())
-    }
-
-    /// Replaces the directory portion of the path with the given byte vector without
-    /// checking for null bytes.
+    /// Replaces the directory portion of the path without checking for null
+    /// bytes.
     /// See `set_dirname` for details.
-    unsafe fn set_dirname_unchecked(&mut self, dirname: &[u8]);
-
-    /// Replaces the directory portion of the path with the given str without
-    /// checking for null bytes.
-    /// See `set_dirname_str` for details.
-    #[inline]
-    unsafe fn set_dirname_str_unchecked(&mut self, dirname: &str) {
-        self.set_dirname_unchecked(dirname.as_bytes())
-    }
+    unsafe fn set_dirname_unchecked<T: BytesContainer>(&mut self, dirname: T);
 
-    /// Replaces the filename portion of the path with the given byte vector without
-    /// checking for null bytes.
+    /// Replaces the filename portion of the path without checking for null
+    /// bytes.
     /// See `set_filename` for details.
-    unsafe fn set_filename_unchecked(&mut self, filename: &[u8]);
-
-    /// Replaces the filename portion of the path with the given str without
-    /// checking for null bytes.
-    /// See `set_filename_str` for details.
-    #[inline]
-    unsafe fn set_filename_str_unchecked(&mut self, filename: &str) {
-        self.set_filename_unchecked(filename.as_bytes())
-    }
+    unsafe fn set_filename_unchecked<T: BytesContainer>(&mut self, filename: T);
 
-    /// Pushes a byte vector onto `self` without checking for null bytes.
+    /// Pushes a path onto `self` without checking for null bytes.
     /// See `push` for details.
-    unsafe fn push_unchecked(&mut self, path: &[u8]);
-
-    /// Pushes a str onto `self` without checking for null bytes.
-    /// See `push_str` for details.
-    #[inline]
-    unsafe fn push_str_unchecked(&mut self, path: &str) {
-        self.push_unchecked(path.as_bytes())
-    }
+    unsafe fn push_unchecked<T: BytesContainer>(&mut self, path: T);
 }
 
 /// Helper struct for printing paths with format!()
@@ -883,6 +778,86 @@ impl<'self, P: GenericPath> fmt::Default for FilenameDisplay<'self, P> {
     }
 }
 
+impl<'self> BytesContainer for &'self str {
+    #[inline]
+    fn container_as_bytes<'a>(&'a self) -> &'a [u8] {
+        self.as_bytes()
+    }
+    #[inline]
+    fn container_as_str<'a>(&'a self) -> &'a str {
+        *self
+    }
+    #[inline]
+    fn container_as_str_opt<'a>(&'a self) -> Option<&'a str> {
+        Some(*self)
+    }
+    #[inline]
+    fn is_str(_: Option<&'self str>) -> bool { true }
+}
+
+impl BytesContainer for ~str {
+    #[inline]
+    fn container_as_bytes<'a>(&'a self) -> &'a [u8] {
+        self.as_bytes()
+    }
+    #[inline]
+    fn container_into_owned_bytes(self) -> ~[u8] {
+        self.into_bytes()
+    }
+    #[inline]
+    fn container_as_str<'a>(&'a self) -> &'a str {
+        self.as_slice()
+    }
+    #[inline]
+    fn container_as_str_opt<'a>(&'a self) -> Option<&'a str> {
+        Some(self.as_slice())
+    }
+    #[inline]
+    fn is_str(_: Option<~str>) -> bool { true }
+}
+
+impl BytesContainer for @str {
+    #[inline]
+    fn container_as_bytes<'a>(&'a self) -> &'a [u8] {
+        self.as_bytes()
+    }
+    #[inline]
+    fn container_as_str<'a>(&'a self) -> &'a str {
+        self.as_slice()
+    }
+    #[inline]
+    fn container_as_str_opt<'a>(&'a self) -> Option<&'a str> {
+        Some(self.as_slice())
+    }
+    #[inline]
+    fn is_str(_: Option<@str>) -> bool { true }
+}
+
+impl<'self> BytesContainer for &'self [u8] {
+    #[inline]
+    fn container_as_bytes<'a>(&'a self) -> &'a [u8] {
+        *self
+    }
+}
+
+impl BytesContainer for ~[u8] {
+    #[inline]
+    fn container_as_bytes<'a>(&'a self) -> &'a [u8] {
+        self.as_slice()
+    }
+    #[inline]
+    fn container_into_owned_bytes(self) -> ~[u8] {
+        self
+    }
+}
+
+impl BytesContainer for @[u8] {
+    #[inline]
+    fn container_as_bytes<'a>(&'a self) -> &'a [u8] {
+        self.as_slice()
+    }
+}
+
 #[inline(always)]
 fn contains_nul(v: &[u8]) -> bool {
     v.iter().any(|&x| x == 0)