diff --git a/library/std/src/path.rs b/library/std/src/path.rs index bf27df7b04281..f8e8ab0c56725 100644 --- a/library/std/src/path.rs +++ b/library/std/src/path.rs @@ -604,7 +604,7 @@ impl AsRef for Component<'_> { /// ``` /// /// [`components`]: Path::components -#[derive(Clone)] +#[derive(Copy, Clone)] #[must_use = "iterators are lazy and do nothing unless consumed"] #[stable(feature = "rust1", since = "1.0.0")] pub struct Components<'a> { @@ -1111,7 +1111,12 @@ fn compare_components(mut left: Components<'_>, mut right: Components<'_>) -> cm #[must_use = "iterators are lazy and do nothing unless consumed"] #[stable(feature = "path_ancestors", since = "1.28.0")] pub struct Ancestors<'a> { - next: Option<&'a Path>, + path: &'a [u8], + components: Components<'a>, + front_bytes: usize, + back_bytes: usize, + trailing_seps: usize, + is_relative: bool, } #[stable(feature = "path_ancestors", since = "1.28.0")] @@ -1120,9 +1125,95 @@ impl<'a> Iterator for Ancestors<'a> { #[inline] fn next(&mut self) -> Option { - let next = self.next; - self.next = next.and_then(Path::parent); - next + let path_len = self.path.len(); + // We reach here when we no longer have anymore paths + // to consume, we're dealing with relative paths and + // need to output "" + if self.front_bytes + self.back_bytes >= path_len - self.trailing_seps { + if self.is_relative { + self.is_relative = false; + return Some(Path::new("")); + } + return None; + } + + // `Ancestors::next` presents the current path + let curr_back_bytes = self.back_bytes; + let curr_path_bytes = self.components.as_path().as_u8_slice(); + + // Consume back component + self.components.next_back(); + let next_path_bytes = self.components.as_path().as_u8_slice(); + self.back_bytes += curr_path_bytes.len() - next_path_bytes.len(); + + // `Ancestors::next` first path to present will always be the entire + // path untrimmed + if curr_back_bytes == 0 { + // SAFETY: This contains the whole original path + let sliced_path = unsafe { Path::from_u8_slice(&self.path[0..path_len]) }; + return Some(Path::new(sliced_path)); + } + + let back_ind = path_len - self.trailing_seps - curr_back_bytes; + // SAFETY: Traversing through component should stop at a valid separator byte + // so this should always be a valid u8 slice + let sliced_path = unsafe { Path::from_u8_slice(&self.path[0..back_ind]) }; + // we use `Path::components` here instead of `Path::trim_trailing_seps` because + // the latter method does not normalize curr dir components (i.e. "/foo////.////bar") + Some(Path::components(sliced_path).as_path()) + } +} + +#[stable(feature = "reverse_ancestors", since = "CURRENT_RUSTC_VERSION")] +impl<'a> DoubleEndedIterator for Ancestors<'a> { + #[inline] + fn next_back(&mut self) -> Option { + let path_len = self.path.len(); + // We reach here when we no longer have anymore paths + // to consume, we're dealing with relative paths and + // need to output "" + if self.front_bytes + self.back_bytes >= path_len - self.trailing_seps { + // This is needed for mixing `Ancestors::next`/`Ancestors::next_back` + // on "." directory + if self.is_relative { + self.is_relative = false; + return Some(Path::new("")); + } + return None; + } + + // If path is relative, the first path that `Ancestors::next_back` + // produce is an empty path + if self.is_relative { + self.is_relative = false; + return Some(Path::new("")); + } + + // `Ancestors::next_back` presents the path given by the next consumed + // components + let curr_path_bytes = self.components.as_path().as_u8_slice(); + // Consume front component + self.components.next(); + let next_path_bytes = self.components.as_path().as_u8_slice(); + + // We add up how many bytes we have read between curr_path_bytes and + // next_path_bytes to check if our `Ancestors::next_back` is presenting + // the last path component (in which case we need to present the whole + // path untrimmed) + self.front_bytes += curr_path_bytes.len() - next_path_bytes.len(); + if self.front_bytes == path_len - self.trailing_seps { + self.front_bytes += self.trailing_seps; + // SAFETY: This contains the whole original path + let sliced_path = unsafe { Path::from_u8_slice(&self.path[0..path_len]) }; + return Some(Path::new(sliced_path)); + } + + // SAFETY: Traversing through component should stop at a valid separator byte + // so this should always be a valid u8 slice + let sliced_path = unsafe { Path::from_u8_slice(&self.path[0..self.front_bytes]) }; + // we use `Path::components` here instead of `Path::trim_trailing_seps` because + // the latter method does not normalize curr dir components (i.e. "/foo////.////bar") + Some(Path::components(sliced_path).as_path()) } } @@ -2616,11 +2707,13 @@ impl Path { /// Produces an iterator over `Path` and its ancestors. /// - /// The iterator will yield the `Path` that is returned if the [`parent`] method is used zero - /// or more times. If the [`parent`] method returns [`None`], the iterator will do likewise. /// The iterator will always yield at least one value, namely `Some(&self)`. Next it will yield /// `&self.parent()`, `&self.parent().and_then(Path::parent)` and so on. /// + /// The iterator also allows you to yield `Path`(s) in the forward direction using + /// `.next_back()` or `.rev().next()`. It will always be symmetrical with the `.next()` + /// direction. + /// /// # Examples /// /// ``` @@ -2640,11 +2733,38 @@ impl Path { /// assert_eq!(ancestors.next(), None); /// ``` /// + /// ``` + /// use std::path::Path; + /// + /// let mut ancestors = Path::new("/foo/bar").ancestors(); + /// assert_eq!(ancestors.next_back(), Some(Path::new("/"))); + /// assert_eq!(ancestors.next_back(), Some(Path::new("/foo"))); + /// assert_eq!(ancestors.next_back(), Some(Path::new("/foo/bar"))); + /// assert_eq!(ancestors.next_back(), None); + /// + /// let mut ancestors = Path::new("../foo/bar").ancestors(); + /// assert_eq!(ancestors.next_back(), Some(Path::new(""))); + /// assert_eq!(ancestors.next_back(), Some(Path::new(".."))); + /// assert_eq!(ancestors.next_back(), Some(Path::new("../foo"))); + /// assert_eq!(ancestors.next_back(), Some(Path::new("../foo/bar"))); + /// assert_eq!(ancestors.next_back(), None); + /// ``` + /// /// [`parent`]: Path::parent #[stable(feature = "path_ancestors", since = "1.28.0")] #[inline] pub fn ancestors(&self) -> Ancestors<'_> { - Ancestors { next: Some(&self) } + let path = self.as_os_str().as_encoded_bytes(); + let trailing_seps = path.len() - self.components().as_path().as_u8_slice().len(); + let is_relative = self.is_relative(); + Ancestors { + path, + components: self.components(), + front_bytes: 0, + back_bytes: 0, + trailing_seps, + is_relative, + } } /// Returns the final component of the `Path`, if there is one. diff --git a/library/std/tests/path_ancestors.rs b/library/std/tests/path_ancestors.rs new file mode 100644 index 0000000000000..697c3910e3a79 --- /dev/null +++ b/library/std/tests/path_ancestors.rs @@ -0,0 +1,459 @@ +use std::ffi::OsStr; +use std::path::Path; + +#[test] +fn empty_path_ancestors() { + let path = Path::new(""); + + let mut ancestors = path.ancestors(); + assert_eq!(ancestors.next().unwrap().as_os_str(), OsStr::new("")); + assert_eq!(ancestors.next(), None); + + let mut rev_ancestors = path.ancestors(); + assert_eq!(rev_ancestors.next_back().unwrap().as_os_str(), OsStr::new("")); + assert_eq!(ancestors.next(), None); +} + +#[test] +fn curr_dir_only_path_ancestors() { + let path = Path::new("."); + let mut ancestors = path.ancestors(); + assert_eq!(ancestors.next().unwrap().as_os_str(), OsStr::new(".")); + assert_eq!(ancestors.next().unwrap().as_os_str(), OsStr::new("")); + assert_eq!(ancestors.next(), None); + + let mut rev_ancestors = path.ancestors(); + assert_eq!(rev_ancestors.next_back().unwrap().as_os_str(), OsStr::new("")); + assert_eq!(rev_ancestors.next_back().unwrap().as_os_str(), OsStr::new(".")); + assert_eq!(rev_ancestors.next_back(), None); +} + +#[test] +fn curr_dir_only_path_ancestors_rev() { + let path = Path::new("."); + let mut ancestors = path.ancestors(); + assert_eq!(ancestors.next().unwrap().as_os_str(), OsStr::new(".")); + // next_back() should only see "" leftover + assert_eq!(ancestors.next_back().unwrap().as_os_str(), OsStr::new("")); + // We have consumed "." and "", we should only observe + // `None` being returned from either end + assert_eq!(ancestors.next(), None); + assert_eq!(ancestors.next_back(), None); + + // operates like next_back() + let mut rev_ancestors = path.ancestors().rev(); + assert_eq!(rev_ancestors.next().unwrap().as_os_str(), OsStr::new("")); + + // operates like next() + let mut rev_ancestors = rev_ancestors.rev(); + assert_eq!(rev_ancestors.next().unwrap().as_os_str(), OsStr::new(".")); + + // fully consumed, should return None + let mut rev_ancestors = rev_ancestors.rev(); + assert_eq!(rev_ancestors.next_back(), None); +} + +#[test] +fn curr_dir_only_path_ancestors_rev_2() { + let path = Path::new("."); + let mut ancestors = path.ancestors(); + assert_eq!(ancestors.next_back().unwrap().as_os_str(), OsStr::new("")); + // next_back() should only see "." leftover + assert_eq!(ancestors.next().unwrap().as_os_str(), OsStr::new(".")); + // We have consumed "" and ".", we should only observe + // `None` being returned from either end + assert_eq!(ancestors.next(), None); + assert_eq!(ancestors.next_back(), None); + + // operates like next() + let mut rev_ancestors = path.ancestors().rev(); + assert_eq!(rev_ancestors.next_back().unwrap().as_os_str(), OsStr::new(".")); + + // operates like next_back() + let mut rev_ancestors = rev_ancestors.rev(); + assert_eq!(rev_ancestors.next().unwrap().as_os_str(), OsStr::new("")); + + // fully consumed, should return None + let mut rev_ancestors = rev_ancestors.rev(); + assert_eq!(rev_ancestors.next_back(), None); +} + +#[test] +fn single_letter_path_ancestors() { + let path = Path::new("a"); + let mut ancestors = path.ancestors(); + assert_eq!(ancestors.next().unwrap().as_os_str(), OsStr::new("a")); + assert_eq!(ancestors.next().unwrap().as_os_str(), OsStr::new("")); + assert_eq!(ancestors.next(), None); + + let mut rev_ancestors = path.ancestors(); + assert_eq!(rev_ancestors.next_back().unwrap().as_os_str(), OsStr::new("")); + assert_eq!(rev_ancestors.next_back().unwrap().as_os_str(), OsStr::new("a")); + assert_eq!(rev_ancestors.next_back(), None); +} + +#[test] +fn single_letter_trailing_path_ancestors() { + let path = Path::new("a/"); + let mut ancestors = path.ancestors(); + assert_eq!(ancestors.next().unwrap().as_os_str(), OsStr::new("a/")); + assert_eq!(ancestors.next().unwrap().as_os_str(), OsStr::new("")); + assert_eq!(ancestors.next(), None); + + let mut rev_ancestors = path.ancestors(); + assert_eq!(rev_ancestors.next_back().unwrap().as_os_str(), OsStr::new("")); + assert_eq!(rev_ancestors.next_back().unwrap().as_os_str(), OsStr::new("a/")); + assert_eq!(rev_ancestors.next_back(), None); +} + +#[test] +fn curr_dir_relative_path_ancestors() { + let path = Path::new("./foo/bar"); + let mut ancestors = path.ancestors(); + assert_eq!(ancestors.next().unwrap().as_os_str(), OsStr::new("./foo/bar")); + assert_eq!(ancestors.next().unwrap().as_os_str(), OsStr::new("./foo")); + assert_eq!(ancestors.next().unwrap().as_os_str(), OsStr::new(".")); + assert_eq!(ancestors.next().unwrap().as_os_str(), OsStr::new("")); + assert_eq!(ancestors.next(), None); + + let mut rev_ancestors = path.ancestors(); + assert_eq!(rev_ancestors.next_back().unwrap().as_os_str(), OsStr::new("")); + assert_eq!(rev_ancestors.next_back().unwrap().as_os_str(), OsStr::new(".")); + assert_eq!(rev_ancestors.next_back().unwrap().as_os_str(), OsStr::new("./foo")); + assert_eq!(rev_ancestors.next_back().unwrap().as_os_str(), OsStr::new("./foo/bar")); + assert_eq!(rev_ancestors.next_back(), None); +} + +#[test] +fn multiple_curr_dir_relative_path_ancestors() { + let path = Path::new("././././baz/beam/boo"); + let mut ancestors = path.ancestors(); + assert_eq!(ancestors.next().unwrap().as_os_str(), OsStr::new("././././baz/beam/boo")); + assert_eq!(ancestors.next().unwrap().as_os_str(), OsStr::new("././././baz/beam")); + assert_eq!(ancestors.next().unwrap().as_os_str(), OsStr::new("././././baz")); + assert_eq!(ancestors.next().unwrap().as_os_str(), OsStr::new(".")); + assert_eq!(ancestors.next().unwrap().as_os_str(), OsStr::new("")); + assert_eq!(ancestors.next(), None); + + let mut rev_ancestors = path.ancestors(); + assert_eq!(rev_ancestors.next_back().unwrap().as_os_str(), OsStr::new("")); + assert_eq!(rev_ancestors.next_back().unwrap().as_os_str(), OsStr::new(".")); + assert_eq!(rev_ancestors.next_back().unwrap().as_os_str(), OsStr::new("././././baz")); + assert_eq!(rev_ancestors.next_back().unwrap().as_os_str(), OsStr::new("././././baz/beam")); + assert_eq!(rev_ancestors.next_back().unwrap().as_os_str(), OsStr::new("././././baz/beam/boo")); + assert_eq!(rev_ancestors.next_back(), None); +} + +#[test] +fn parent_dir_only_path_ancestors() { + let path = Path::new(".."); + let mut ancestors = path.ancestors(); + assert_eq!(ancestors.next().unwrap().as_os_str(), OsStr::new("..")); + assert_eq!(ancestors.next().unwrap().as_os_str(), OsStr::new("")); + assert_eq!(ancestors.next(), None); + + let mut rev_ancestors = path.ancestors(); + assert_eq!(rev_ancestors.next_back().unwrap().as_os_str(), OsStr::new("")); + assert_eq!(rev_ancestors.next_back().unwrap().as_os_str(), OsStr::new("..")); + assert_eq!(rev_ancestors.next_back(), None); +} + +#[test] +fn parent_dir_relative_path_ancestors() { + let path = Path::new("../foo/bar/"); + let mut ancestors = path.ancestors(); + assert_eq!(ancestors.next().unwrap().as_os_str(), OsStr::new("../foo/bar/")); + assert_eq!(ancestors.next().unwrap().as_os_str(), OsStr::new("../foo")); + assert_eq!(ancestors.next().unwrap().as_os_str(), OsStr::new("..")); + assert_eq!(ancestors.next().unwrap().as_os_str(), OsStr::new("")); + assert_eq!(ancestors.next(), None); + + let mut rev_ancestors = path.ancestors(); + assert_eq!(rev_ancestors.next_back().unwrap().as_os_str(), OsStr::new("")); + assert_eq!(rev_ancestors.next_back().unwrap().as_os_str(), OsStr::new("..")); + assert_eq!(rev_ancestors.next_back().unwrap().as_os_str(), OsStr::new("../foo")); + assert_eq!(rev_ancestors.next_back().unwrap().as_os_str(), OsStr::new("../foo/bar/")); + assert_eq!(rev_ancestors.next_back(), None); +} + +#[test] +fn relative_path_ancestors() { + let path = Path::new("foo/bar/baz/"); + let mut ancestors = path.ancestors(); + assert_eq!(ancestors.next().unwrap().as_os_str(), OsStr::new("foo/bar/baz/")); + assert_eq!(ancestors.next().unwrap().as_os_str(), OsStr::new("foo/bar")); + assert_eq!(ancestors.next().unwrap().as_os_str(), OsStr::new("foo")); + assert_eq!(ancestors.next().unwrap().as_os_str(), OsStr::new("")); + assert_eq!(ancestors.next(), None); + + let mut rev_ancestors = path.ancestors(); + assert_eq!(rev_ancestors.next_back().unwrap().as_os_str(), OsStr::new("")); + assert_eq!(rev_ancestors.next_back().unwrap().as_os_str(), OsStr::new("foo")); + assert_eq!(rev_ancestors.next_back().unwrap().as_os_str(), OsStr::new("foo/bar")); + assert_eq!(rev_ancestors.next_back().unwrap().as_os_str(), OsStr::new("foo/bar/baz/")); + assert_eq!(rev_ancestors.next_back(), None); +} + +#[test] +fn one_letter_relative_path_ancestors() { + let path = Path::new("a/"); + let mut ancestors = path.ancestors(); + assert_eq!(ancestors.next().unwrap().as_os_str(), OsStr::new("a/")); + assert_eq!(ancestors.next().unwrap().as_os_str(), OsStr::new("")); + assert_eq!(ancestors.next(), None); + + let mut rev_ancestors = path.ancestors(); + assert_eq!(rev_ancestors.next_back().unwrap().as_os_str(), OsStr::new("")); + assert_eq!(rev_ancestors.next_back().unwrap().as_os_str(), OsStr::new("a/")); + assert_eq!(rev_ancestors.next_back(), None); +} + +#[test] +fn root_dir_only_path_ancestors() { + let path = Path::new("/"); + let mut ancestors = path.ancestors(); + assert_eq!(ancestors.next().unwrap().as_os_str(), OsStr::new("/")); + assert_eq!(ancestors.next(), None); + + let mut rev_ancestors = path.ancestors(); + assert_eq!(rev_ancestors.next_back().unwrap().as_os_str(), OsStr::new("/")); + assert_eq!(rev_ancestors.next_back(), None); +} + +#[test] +fn root_dir_trailing_path_ancestors() { + let path = Path::new("////"); + let mut ancestors = path.ancestors(); + assert_eq!(ancestors.next().unwrap().as_os_str(), OsStr::new("////")); + assert_eq!(ancestors.next(), None); + + let mut rev_ancestors = path.ancestors(); + assert_eq!(rev_ancestors.next_back().unwrap().as_os_str(), OsStr::new("////")); + assert_eq!(rev_ancestors.next_back(), None); +} + +#[test] +fn absolute_path_ancestors() { + let path = Path::new("/foo/bar/"); + let mut ancestors = path.ancestors(); + assert_eq!(ancestors.next().unwrap().as_os_str(), OsStr::new("/foo/bar/")); + assert_eq!(ancestors.next().unwrap().as_os_str(), OsStr::new("/foo")); + assert_eq!(ancestors.next().unwrap().as_os_str(), OsStr::new("/")); + assert_eq!(ancestors.next(), None); + + let mut rev_ancestors = path.ancestors(); + assert_eq!(rev_ancestors.next_back().unwrap().as_os_str(), OsStr::new("/")); + assert_eq!(rev_ancestors.next_back().unwrap().as_os_str(), OsStr::new("/foo")); + assert_eq!(rev_ancestors.next_back().unwrap().as_os_str(), OsStr::new("/foo/bar/")); + assert_eq!(rev_ancestors.next_back(), None); +} + +#[test] +fn absolute_path_with_curr_dir_path_ancestors() { + let path = Path::new("/."); + let mut ancestors = path.ancestors(); + assert_eq!(ancestors.next().unwrap().as_os_str(), OsStr::new("/.")); + assert_eq!(ancestors.next(), None); + + let mut rev_ancestors = path.ancestors(); + assert_eq!(rev_ancestors.next_back().unwrap().as_os_str(), OsStr::new("/.")); + assert_eq!(rev_ancestors.next_back(), None); +} + +#[test] +fn absolute_path_with_trailing_curr_dir_path_ancestors() { + let path = Path::new("/./././././."); + let mut ancestors = path.ancestors(); + assert_eq!(ancestors.next().unwrap().as_os_str(), OsStr::new("/./././././.")); + assert_eq!(ancestors.next(), None); + + let mut rev_ancestors = path.ancestors(); + assert_eq!(rev_ancestors.next_back().unwrap().as_os_str(), OsStr::new("/./././././.")); + assert_eq!(rev_ancestors.next_back(), None); +} + +#[test] +fn absolute_path_with_parent_dir_path_ancestors() { + let path = Path::new("/.."); + let mut ancestors = path.ancestors(); + assert_eq!(ancestors.next().unwrap().as_os_str(), OsStr::new("/..")); + assert_eq!(ancestors.next().unwrap().as_os_str(), OsStr::new("/")); + assert_eq!(ancestors.next(), None); + + let mut rev_ancestors = path.ancestors(); + assert_eq!(rev_ancestors.next_back().unwrap().as_os_str(), OsStr::new("/")); + assert_eq!(rev_ancestors.next_back().unwrap().as_os_str(), OsStr::new("/..")); + assert_eq!(rev_ancestors.next_back(), None); +} + +#[test] +fn absolute_with_in_between_trailing_seps_path_ancestors() { + let path = Path::new("/foo/////bar/"); + let mut ancestors = path.ancestors(); + assert_eq!(ancestors.next().unwrap().as_os_str(), OsStr::new("/foo/////bar/")); + assert_eq!(ancestors.next().unwrap().as_os_str(), OsStr::new("/foo")); + assert_eq!(ancestors.next().unwrap().as_os_str(), OsStr::new("/")); + assert_eq!(ancestors.next(), None); + + let mut rev_ancestors = path.ancestors(); + assert_eq!(rev_ancestors.next_back().unwrap().as_os_str(), OsStr::new("/")); + assert_eq!(rev_ancestors.next_back().unwrap().as_os_str(), OsStr::new("/foo")); + assert_eq!(rev_ancestors.next_back().unwrap().as_os_str(), OsStr::new("/foo/////bar/")); + assert_eq!(rev_ancestors.next_back(), None); +} + +#[test] +fn absolute_curr_dir_and_trailing_seps_path_ancestors() { + let path = Path::new("/foo/bar/./././."); + let mut ancestors = path.ancestors(); + assert_eq!(ancestors.next().unwrap().as_os_str(), OsStr::new("/foo/bar/./././.")); + assert_eq!(ancestors.next().unwrap().as_os_str(), OsStr::new("/foo")); + assert_eq!(ancestors.next().unwrap().as_os_str(), OsStr::new("/")); + assert_eq!(ancestors.next(), None); + + let mut rev_ancestors = path.ancestors(); + assert_eq!(rev_ancestors.next_back().unwrap().as_os_str(), OsStr::new("/")); + assert_eq!(rev_ancestors.next_back().unwrap().as_os_str(), OsStr::new("/foo")); + assert_eq!(rev_ancestors.next_back().unwrap().as_os_str(), OsStr::new("/foo/bar/./././.")); + assert_eq!(rev_ancestors.next_back(), None); +} + +#[test] +fn absolute_curr_dir_and_in_between_trailing_seps_path_ancestors() { + let path = Path::new("/foo////.////bar"); + let mut ancestors = path.ancestors(); + assert_eq!(ancestors.next().unwrap().as_os_str(), OsStr::new("/foo////.////bar")); + assert_eq!(ancestors.next().unwrap().as_os_str(), OsStr::new("/foo")); + assert_eq!(ancestors.next().unwrap().as_os_str(), OsStr::new("/")); + assert_eq!(ancestors.next(), None); + + let mut rev_ancestors = path.ancestors(); + assert_eq!(rev_ancestors.next_back().unwrap().as_os_str(), OsStr::new("/")); + assert_eq!(rev_ancestors.next_back().unwrap().as_os_str(), OsStr::new("/foo")); + assert_eq!(rev_ancestors.next_back().unwrap().as_os_str(), OsStr::new("/foo////.////bar")); + assert_eq!(rev_ancestors.next_back(), None); +} + +#[test] +fn absolute_rev_path_ancestors() { + let path = Path::new("/foo/bar/baz/"); + let mut ancestors = path.ancestors(); + + assert_eq!(ancestors.next().unwrap().as_os_str(), OsStr::new("/foo/bar/baz/")); + assert_eq!(ancestors.next().unwrap().as_os_str(), OsStr::new("/foo/bar")); + assert_eq!(ancestors.next_back().unwrap().as_os_str(), OsStr::new("/")); + assert_eq!(ancestors.next_back().unwrap().as_os_str(), OsStr::new("/foo")); + assert_eq!(ancestors.next(), None); // Fully consumed + assert_eq!(ancestors.next_back(), None); // Fully consumed +} + +#[cfg(windows)] +#[test] +fn verbatim_prefix_component_path_ancestors() { + let path = Path::new(r"\\\\?\\UNC\\server\\share\\.."); + let mut ancestors = path.ancestors(); + assert_eq!(ancestors.next().unwrap().as_os_str(), OsStr::new(r"\\\\?\\UNC\\server\\share\\..")); + assert_eq!(ancestors.next().unwrap().as_os_str(), OsStr::new(r"\\\\?\\UNC\\server\\share\\")); + assert_eq!(ancestors.next(), None); + + let mut ancestors = path.ancestors(); + assert_eq!( + ancestors.next_back().unwrap().as_os_str(), + OsStr::new(r"\\\\?\\UNC\\server\\share\\") + ); + assert_eq!( + ancestors.next_back().unwrap().as_os_str(), + OsStr::new(r"\\\\?\\UNC\\server\\share\\..") + ); + assert_eq!(ancestors.next_back(), None); +} + +#[cfg(windows)] +#[test] +fn verbatim_unc_prefix_component_path_ancestors() { + let path = Path::new(r"\\?\pictures\kittens"); + let mut ancestors = path.ancestors(); + assert_eq!(ancestors.next().unwrap().as_os_str(), OsStr::new(r"\\?\pictures\kittens")); + assert_eq!(ancestors.next().unwrap().as_os_str(), OsStr::new(r"\\?\pictures")); + assert_eq!(ancestors.next(), None); + + let mut ancestors = path.ancestors(); + assert_eq!(ancestors.next_back().unwrap().as_os_str(), OsStr::new(r"\\?\pictures\")); + assert_eq!(ancestors.next_back().unwrap().as_os_str(), OsStr::new(r"\\?\pictures\kittens")); + assert_eq!(ancestors.next_back(), None); +} + +#[cfg(windows)] +#[test] +fn verbatim_disk_prefix_component_path_ancestors() { + let path = Path::new(r"\\?\c:\Test"); + let mut ancestors = path.ancestors(); + assert_eq!(ancestors.next().unwrap().as_os_str(), OsStr::new(r"\\?\c:\Test")); + assert_eq!(ancestors.next().unwrap().as_os_str(), OsStr::new(r"\\?\c:\")); + assert_eq!(ancestors.next(), None); + + let mut ancestors = path.ancestors(); + assert_eq!(ancestors.next_back().unwrap().as_os_str(), OsStr::new(r"\\?\c:\")); + assert_eq!(ancestors.next_back().unwrap().as_os_str(), OsStr::new(r"\\?\c:\Test")); + assert_eq!(ancestors.next_back(), None); +} + +#[cfg(windows)] +#[test] +fn device_ns_prefix_component_path_ancestors() { + // No this will not execute notepad.exe + let path = Path::new(r"\\.\c:\Windows\System32\notepad.exe"); + let mut ancestors = path.ancestors(); + assert_eq!( + ancestors.next().unwrap().as_os_str(), + OsStr::new(r"\\.\c:\Windows\System32\notepad.exe") + ); + assert_eq!(ancestors.next().unwrap().as_os_str(), OsStr::new(r"\\.\c:\Windows\System32")); + assert_eq!(ancestors.next().unwrap().as_os_str(), OsStr::new(r"\\.\c:\Windows")); + assert_eq!(ancestors.next().unwrap().as_os_str(), OsStr::new(r"\\.\c:\")); + assert_eq!(ancestors.next(), None); + + let mut ancestors = path.ancestors(); + assert_eq!(ancestors.next_back().unwrap().as_os_str(), OsStr::new(r"\\.\c:")); + assert_eq!(ancestors.next_back().unwrap().as_os_str(), OsStr::new(r"\\.\c:\Windows")); + assert_eq!(ancestors.next_back().unwrap().as_os_str(), OsStr::new(r"\\.\c:\Windows\System32")); + assert_eq!( + ancestors.next_back().unwrap().as_os_str(), + OsStr::new(r"\\.\c:\Windows\System32\notepad.exe") + ); + assert_eq!(ancestors.next_back(), None); +} + +#[cfg(windows)] +#[test] +fn unc_prefix_component_path_ancestors() { + let path = Path::new(r"\\server\share\test"); + let mut ancestors = path.ancestors(); + assert_eq!(ancestors.next().unwrap().as_os_str(), OsStr::new(r"\\server\share\test")); + assert_eq!(ancestors.next().unwrap().as_os_str(), OsStr::new(r"\\server\share")); + assert_eq!(ancestors.next(), None); + + let mut ancestors = path.ancestors(); + assert_eq!(ancestors.next_back().unwrap().as_os_str(), OsStr::new(r"\\server\share")); + assert_eq!(ancestors.next_back().unwrap().as_os_str(), OsStr::new(r"\\server\share\test")); + assert_eq!(ancestors.next_back(), None); +} + +#[cfg(windows)] +#[test] +fn disk_prefix_component_path_ancestors() { + let path = Path::new(r"C:a\..\.."); + let mut ancestors = path.ancestors(); + assert_eq!(ancestors.next().unwrap().as_os_str(), OsStr::new(r"C:a\..\..")); + assert_eq!(ancestors.next().unwrap().as_os_str(), OsStr::new(r"C:a\..")); + assert_eq!(ancestors.next().unwrap().as_os_str(), OsStr::new(r"C:a")); + assert_eq!(ancestors.next().unwrap().as_os_str(), OsStr::new(r"C:")); + assert_eq!(ancestors.next(), None); + + let mut ancestors = path.ancestors(); + assert_eq!(ancestors.next_back().unwrap().as_os_str(), OsStr::new(r"C:")); + assert_eq!(ancestors.next_back().unwrap().as_os_str(), OsStr::new(r"C:a")); + assert_eq!(ancestors.next_back().unwrap().as_os_str(), OsStr::new(r"C:a\..")); + assert_eq!(ancestors.next_back().unwrap().as_os_str(), OsStr::new(r"C:a\..\..")); + assert_eq!(ancestors.next_back(), None); +}