1use std::{
28 ffi::OsStr,
29 path::{Component, Path},
30};
31
32use crate::trailing_slash::has_trailing_slash;
33
34pub fn components(path: &Path) -> impl Iterator<Item = Component<'_>> {
54 let mut final_component = Some(Component::Normal(OsStr::new("")));
55 path.components().chain(std::iter::from_fn(move || {
56 if has_trailing_slash(path) {
57 final_component.take()
58 } else {
59 None
60 }
61 }))
62}
63
64#[cfg(test)]
65mod test {
66 use crate::assert_path_eq;
70 use std::{
71 ffi::OsStr,
72 path::{Component, Path, PathBuf},
73 };
74
75 #[test]
76 fn empty_path() {
77 let path = Path::new("");
78 let mut components = crate::components(path);
79
80 assert_eq!(components.next(), None);
81 }
82
83 #[test]
84 #[cfg(windows)]
85 fn prefix_only() {
86 let path = Path::new("C:");
87 let mut components = crate::components(path);
88
89 assert!(matches!(components.next(), Some(Component::Prefix(_))));
90 assert_eq!(components.next(), None);
91 }
92
93 #[test]
94 #[cfg(windows)]
95 fn prefix_with_trailing_slash() {
96 let path = Path::new("C:\\");
97 let mut components = crate::components(path);
98
99 assert!(matches!(components.next(), Some(Component::Prefix(_))));
100 assert!(matches!(components.next(), Some(Component::RootDir)));
101 assert_eq!(components.next(), Some(Component::Normal(OsStr::new(""))));
102 assert_eq!(components.next(), None);
103 }
104
105 #[test]
106 fn root() {
107 let path = Path::new("/");
108 let mut components = crate::components(path);
109
110 assert!(matches!(components.next(), Some(Component::RootDir)));
111 assert_eq!(components.next(), Some(Component::Normal(OsStr::new(""))));
112 assert_eq!(components.next(), None);
113 }
114
115 #[test]
116 fn cur_dir_only() {
117 let path = Path::new(".");
118 let mut components = crate::components(path);
119
120 assert!(matches!(components.next(), Some(Component::CurDir)));
121 assert_eq!(components.next(), None);
122 }
123
124 #[test]
125 fn cur_dir_with_trailing_slash() {
126 let path = Path::new("./");
127 let mut components = crate::components(path);
128
129 assert!(matches!(components.next(), Some(Component::CurDir)));
130 assert_eq!(components.next(), Some(Component::Normal(OsStr::new(""))));
131 assert_eq!(components.next(), None);
132 }
133
134 #[test]
135 fn parent_dir_only() {
136 let path = Path::new("..");
137 let mut components = crate::components(path);
138
139 assert!(matches!(components.next(), Some(Component::ParentDir)));
140 assert_eq!(components.next(), None);
141 }
142
143 #[test]
144 fn parent_dir_with_trailing_slash() {
145 let path = Path::new("../");
146 let mut components = crate::components(path);
147
148 assert!(matches!(components.next(), Some(Component::ParentDir)));
149 assert_eq!(components.next(), Some(Component::Normal(OsStr::new(""))));
150 assert_eq!(components.next(), None);
151 }
152
153 #[test]
154 fn normal_only() {
155 let path = Path::new("foo");
156 let mut components = crate::components(path);
157
158 assert_eq!(
159 components.next(),
160 Some(Component::Normal(OsStr::new("foo")))
161 );
162 assert_eq!(components.next(), None);
163 }
164
165 #[test]
166 fn normal_with_trailing_slash() {
167 let path = Path::new("foo/");
168 let mut components = crate::components(path);
169
170 assert_eq!(
171 components.next(),
172 Some(Component::Normal(OsStr::new("foo")))
173 );
174 assert_eq!(components.next(), Some(Component::Normal(OsStr::new(""))));
175 assert_eq!(components.next(), None);
176 }
177
178 #[test]
179 #[cfg(not(windows))]
180 fn reconstruct_unix_only() {
181 let path = Path::new("/home/Alice");
182
183 let mut buf = PathBuf::new();
184 for component in crate::components(path) {
185 buf.push(component);
186 }
187
188 assert_path_eq!(path, buf);
189 }
190
191 #[test]
192 #[cfg(not(windows))]
193 fn reconstruct_unix_with_trailing_slash() {
194 let path = Path::new("/home/Alice/");
195
196 let mut buf = PathBuf::new();
197 for component in crate::components(path) {
198 buf.push(component);
199 }
200
201 assert_path_eq!(path, buf);
202 }
203
204 #[test]
205 #[cfg(windows)]
206 fn reconstruct_windows_only() {
207 let path = Path::new("C:\\WINDOWS\\System32");
208
209 let mut buf = PathBuf::new();
210 for component in crate::components(path) {
211 buf.push(component);
212 }
213
214 assert_path_eq!(path, buf);
215 }
216
217 #[test]
218 #[cfg(windows)]
219 fn reconstruct_windows_with_trailing_slash() {
220 let path = Path::new("C:\\WINDOWS\\System32\\");
221
222 let mut buf = PathBuf::new();
223 for component in crate::components(path) {
224 buf.push(component);
225 }
226
227 assert_path_eq!(path, buf);
228 }
229}