1use crate::path::{Path, SEPARATOR};
2use core::iter::FusedIterator;
3
4#[derive(Clone)]
5pub struct Components<'a> {
6 path: &'a str,
7 state_front: State,
8 state_back: State,
9}
10
11#[derive(Clone, Debug, Eq, PartialEq)]
12enum State {
13 StartDir,
14 Body,
15 Done,
16}
17
18impl<'a> Components<'a> {
19 pub fn new(path: &'a Path) -> Self {
20 Components {
21 path: &path.inner,
22 state_front: if path.starts_with(SEPARATOR) {
23 State::StartDir
24 } else {
25 State::Body
26 },
27 state_back: State::Body,
28 }
29 }
30
31 pub fn as_path(&self) -> &'a Path {
32 self.path.into()
33 }
34
35 fn next_component_front(&mut self) -> (usize, Option<Component<'a>>) {
36 debug_assert_eq!(State::Body, self.state_front);
37 if self.path.is_empty() {
38 return (0, None); }
40
41 let (extra, component) = match self
42 .path
43 .as_bytes()
44 .iter()
45 .position(|&b| b as char == SEPARATOR)
46 {
47 None => (0, self.path),
48 Some(i) => (1, &self.path[..i]),
49 };
50 (component.len() + extra, Some(component.into()))
51 }
52
53 fn next_component_back(&mut self) -> (usize, Option<Component<'a>>) {
54 debug_assert_eq!(State::Body, self.state_back);
55 let (extra, component) = match self
56 .path
57 .as_bytes()
58 .iter()
59 .rposition(|&b| b as char == SEPARATOR)
60 {
61 None => (0, self.path),
62 Some(i) => (1, &self.path[i + 1..]),
63 };
64 (component.len() + extra, Some(component.into()))
65 }
66}
67
68#[derive(Debug, Eq, PartialEq)]
110pub enum Component<'a> {
111 RootDir,
115 CurrentDir,
118 ParentDir,
121 Normal(&'a str),
126}
127
128impl<'a> From<&'a str> for Component<'a> {
129 fn from(s: &'a str) -> Self {
130 match s {
131 "." => Component::CurrentDir,
132 ".." => Component::ParentDir,
133 "/" => Component::RootDir, _ => Component::Normal(s),
135 }
136 }
137}
138
139impl<'a> FusedIterator for Components<'a> {}
140
141impl<'a> Iterator for Components<'a> {
142 type Item = Component<'a>;
143
144 fn next(&mut self) -> Option<Self::Item> {
145 'outer: while !self.path.is_empty() {
146 return match self.state_front {
147 State::StartDir => {
148 self.state_front = State::Body;
149 self.path = &self.path[SEPARATOR.len_utf8()..];
150 Some(Component::RootDir)
151 }
152 State::Body => {
153 if let (count, Some(comp)) = self.next_component_front() {
154 self.path = &self.path[count..];
155 if let Component::Normal("") = comp {
156 continue 'outer; }
158 Some(comp)
159 } else {
160 self.state_front = State::Done;
161 None
162 }
163 }
164 State::Done => None,
165 };
166 }
167 None
168 }
169}
170
171impl<'a> DoubleEndedIterator for Components<'a> {
172 fn next_back(&mut self) -> Option<Self::Item> {
173 'outer: while !self.path.is_empty() {
174 return match self.state_back {
175 State::StartDir => {
176 self.state_back = State::Done;
177 Some(Component::RootDir)
178 }
179 State::Body => {
180 if let (count, Some(comp)) = self.next_component_back() {
181 self.path = &self.path[..self.path.len() - count];
182 if let Component::Normal("") = comp {
183 continue 'outer; }
185 Some(comp)
186 } else {
187 self.state_back = State::Done;
188 None
189 }
190 }
191 State::Done => None,
192 };
193 }
194 None
195 }
196}
197
198#[cfg(test)]
199mod tests {
200 use super::*;
201
202 #[test]
203 fn test_components() {
204 let p = Path::new("hello/world");
205 let mut c = p.components();
206 assert_eq!(Some(Component::Normal("hello")), c.next());
207 assert_eq!(Some(Component::Normal("world")), c.next());
208 assert_eq!(None, c.next());
209 }
210
211 #[test]
212 fn test_components_absolute() {
213 let p = Path::new("/hello/world");
214 let mut c = p.components();
215 assert_eq!(Some(Component::RootDir), c.next());
216 assert_eq!(Some(Component::Normal("hello")), c.next());
217 assert_eq!(Some(Component::Normal("world")), c.next());
218 assert_eq!(None, c.next());
219 }
220
221 #[test]
222 fn test_empty_fragments() {
223 let p = Path::new("hello//////world");
224 let mut c = p.components();
225 assert_eq!(Some(Component::Normal("hello")), c.next());
226 assert_eq!(Some(Component::Normal("world")), c.next());
227 assert_eq!(None, c.next());
228 }
229
230 #[test]
231 fn test_somewhat_fused() {
232 let p = Path::new("/hello");
233 let mut c = p.components();
234 assert_eq!(Some(Component::RootDir), c.next());
235 assert_eq!(Some(Component::Normal("hello")), c.next());
236 for _ in 0..100 {
237 assert_eq!(None, c.next());
238 }
239 }
240
241 #[test]
242 fn test_trailing_slash() {
243 let p = Path::new("/hello/");
244 let mut c = p.components();
245 assert_eq!(Some(Component::RootDir), c.next());
246 assert_eq!(Some(Component::Normal("hello")), c.next());
247 for _ in 0..100 {
248 assert_eq!(None, c.next());
249 }
250 }
251
252 #[test]
253 fn test_trailing_slashes() {
254 let p = Path::new("/hello/////");
255 let mut c = p.components();
256 assert_eq!(Some(Component::RootDir), c.next());
257 assert_eq!(Some(Component::Normal("hello")), c.next());
258 for _ in 0..100 {
259 assert_eq!(None, c.next());
260 }
261 }
262}