use std::{
ffi::OsStr,
path::{Component, Path},
};
use crate::trailing_slash::has_trailing_slash;
pub fn components(path: &Path) -> impl Iterator<Item = Component> {
let mut final_component = Some(Component::Normal(OsStr::new("")));
path.components().chain(std::iter::from_fn(move || {
if has_trailing_slash(path) {
final_component.take()
} else {
None
}
}))
}
#[cfg(test)]
mod test {
use crate::assert_path_eq;
use std::{
ffi::OsStr,
path::{Component, Path, PathBuf},
};
#[test]
fn empty_path() {
let path = Path::new("");
let mut components = crate::components(path);
assert_eq!(components.next(), None);
}
#[test]
#[cfg(windows)]
fn prefix_only() {
let path = Path::new("C:");
let mut components = crate::components(path);
assert!(matches!(components.next(), Some(Component::Prefix(_))));
assert_eq!(components.next(), None);
}
#[test]
#[cfg(windows)]
fn prefix_with_trailing_slash() {
let path = Path::new("C:\\");
let mut components = crate::components(path);
assert!(matches!(components.next(), Some(Component::Prefix(_))));
assert!(matches!(components.next(), Some(Component::RootDir)));
assert_eq!(components.next(), Some(Component::Normal(OsStr::new(""))));
assert_eq!(components.next(), None);
}
#[test]
fn root() {
let path = Path::new("/");
let mut components = crate::components(path);
assert!(matches!(components.next(), Some(Component::RootDir)));
assert_eq!(components.next(), Some(Component::Normal(OsStr::new(""))));
assert_eq!(components.next(), None);
}
#[test]
fn cur_dir_only() {
let path = Path::new(".");
let mut components = crate::components(path);
assert!(matches!(components.next(), Some(Component::CurDir)));
assert_eq!(components.next(), None);
}
#[test]
fn cur_dir_with_trailing_slash() {
let path = Path::new("./");
let mut components = crate::components(path);
assert!(matches!(components.next(), Some(Component::CurDir)));
assert_eq!(components.next(), Some(Component::Normal(OsStr::new(""))));
assert_eq!(components.next(), None);
}
#[test]
fn parent_dir_only() {
let path = Path::new("..");
let mut components = crate::components(path);
assert!(matches!(components.next(), Some(Component::ParentDir)));
assert_eq!(components.next(), None);
}
#[test]
fn parent_dir_with_trailing_slash() {
let path = Path::new("../");
let mut components = crate::components(path);
assert!(matches!(components.next(), Some(Component::ParentDir)));
assert_eq!(components.next(), Some(Component::Normal(OsStr::new(""))));
assert_eq!(components.next(), None);
}
#[test]
fn normal_only() {
let path = Path::new("foo");
let mut components = crate::components(path);
assert_eq!(
components.next(),
Some(Component::Normal(OsStr::new("foo")))
);
assert_eq!(components.next(), None);
}
#[test]
fn normal_with_trailing_slash() {
let path = Path::new("foo/");
let mut components = crate::components(path);
assert_eq!(
components.next(),
Some(Component::Normal(OsStr::new("foo")))
);
assert_eq!(components.next(), Some(Component::Normal(OsStr::new(""))));
assert_eq!(components.next(), None);
}
#[test]
#[cfg(not(windows))]
fn reconstruct_unix_only() {
let path = Path::new("/home/Alice");
let mut buf = PathBuf::new();
for component in crate::components(path) {
buf.push(component);
}
assert_path_eq!(path, buf);
}
#[test]
#[cfg(not(windows))]
fn reconstruct_unix_with_trailing_slash() {
let path = Path::new("/home/Alice/");
let mut buf = PathBuf::new();
for component in crate::components(path) {
buf.push(component);
}
assert_path_eq!(path, buf);
}
#[test]
#[cfg(windows)]
fn reconstruct_windows_only() {
let path = Path::new("C:\\WINDOWS\\System32");
let mut buf = PathBuf::new();
for component in crate::components(path) {
buf.push(component);
}
assert_path_eq!(path, buf);
}
#[test]
#[cfg(windows)]
fn reconstruct_windows_with_trailing_slash() {
let path = Path::new("C:\\WINDOWS\\System32\\");
let mut buf = PathBuf::new();
for component in crate::components(path) {
buf.push(component);
}
assert_path_eq!(path, buf);
}
}