1use alloc::{string::ToString, sync::Arc};
2use core::{fmt, iter::FusedIterator};
3
4#[cfg(feature = "serde")]
5use serde::{Deserialize, Serialize};
6
7use super::{Path, PathError};
8use crate::{ast::Ident, debuginfo::Span};
9
10#[derive(Debug, Clone, Eq, PartialEq, PartialOrd, Ord, Hash)]
15#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
16pub enum PathComponent<'a> {
17 Root,
19 Normal(&'a str),
21}
22
23impl<'a> PathComponent<'a> {
24 #[inline(always)]
26 pub fn as_str(&self) -> &'a str {
27 match self {
28 Self::Root => "::",
29 Self::Normal(id) => id,
30 }
31 }
32
33 #[inline]
35 pub fn to_ident(&self) -> Option<Ident> {
36 match self {
37 Self::Root => None,
38 Self::Normal(id) => Some(Ident::from_raw_parts(Span::unknown(Arc::from(
39 id.to_string().into_boxed_str(),
40 )))),
41 }
42 }
43
44 pub fn char_len(&self) -> usize {
46 match self {
47 Self::Root => 2,
48 Self::Normal(id) => id.chars().count(),
49 }
50 }
51}
52
53impl PartialEq<str> for PathComponent<'_> {
54 fn eq(&self, other: &str) -> bool {
55 self.as_str().eq(other)
56 }
57}
58
59impl AsRef<str> for PathComponent<'_> {
60 #[inline(always)]
61 fn as_ref(&self) -> &str {
62 self.as_str()
63 }
64}
65
66impl fmt::Display for PathComponent<'_> {
67 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
68 f.write_str(self.as_str())
69 }
70}
71
72pub struct Iter<'a> {
87 components: Components<'a>,
88}
89
90impl<'a> Iter<'a> {
91 pub fn new(path: &'a str) -> Self {
92 Self {
93 components: Components {
94 path,
95 front: State::Start,
96 back: State::Body,
97 },
98 }
99 }
100
101 #[inline]
102 pub fn as_path(&self) -> &'a Path {
103 Path::new(self.components.path)
104 }
105}
106
107impl FusedIterator for Iter<'_> {}
108
109impl<'a> Iterator for Iter<'a> {
110 type Item = Result<PathComponent<'a>, PathError>;
111
112 fn next(&mut self) -> Option<Self::Item> {
113 match self.components.next() {
114 Some(Ok(PathComponent::Normal(component)))
115 if component.len() > Path::MAX_COMPONENT_LENGTH =>
116 {
117 Some(Err(PathError::InvalidComponent(crate::ast::IdentError::InvalidLength {
118 max: Path::MAX_COMPONENT_LENGTH,
119 })))
120 },
121 next => next,
122 }
123 }
124}
125
126impl<'a> DoubleEndedIterator for Iter<'a> {
127 fn next_back(&mut self) -> Option<Self::Item> {
128 match self.components.next_back() {
129 Some(Ok(PathComponent::Normal(component)))
130 if component.len() > Path::MAX_COMPONENT_LENGTH =>
131 {
132 Some(Err(PathError::InvalidComponent(crate::ast::IdentError::InvalidLength {
133 max: Path::MAX_COMPONENT_LENGTH,
134 })))
135 },
136 next => next,
137 }
138 }
139}
140
141struct Components<'a> {
143 path: &'a str,
145 front: State,
148 back: State,
149}
150
151#[derive(Debug, Copy, Clone, PartialEq)]
152enum State {
153 Start,
155 Body,
157 QuoteOpened,
159 QuoteClosed,
161 Done,
163}
164
165impl<'a> Components<'a> {
166 fn finished(&self) -> bool {
167 match (self.front, self.back) {
168 (State::Done, _) => true,
169 (_, State::Done) => true,
170 (State::Body | State::QuoteOpened | State::QuoteClosed, State::Start) => true,
171 (..) => false,
172 }
173 }
174}
175
176impl<'a> Iterator for Components<'a> {
177 type Item = Result<PathComponent<'a>, PathError>;
178
179 fn next(&mut self) -> Option<Self::Item> {
180 while !self.finished() {
181 match self.front {
182 State::Start => match self.path.strip_prefix("::") {
183 Some(rest) => {
184 self.path = rest;
185 self.front = State::Body;
186 return Some(Ok(PathComponent::Root));
187 },
188 None if self.path.starts_with(Path::KERNEL_PATH)
189 || self.path.starts_with(Path::EXEC_PATH) =>
190 {
191 self.front = State::Body;
192 return Some(Ok(PathComponent::Root));
193 },
194 None => {
195 self.front = State::Body;
196 },
197 },
198 State::Body => {
199 if let Some(rest) = self.path.strip_prefix('"') {
200 self.front = State::QuoteOpened;
201 self.path = rest;
202 continue;
203 }
204 match self.path.split_once("::") {
205 Some(("", rest)) => {
206 self.path = rest;
207 return Some(Err(PathError::InvalidComponent(
208 crate::ast::IdentError::Empty,
209 )));
210 },
211 Some((component, rest)) => {
212 if rest.is_empty() {
213 self.path = "::";
214 } else {
215 self.path = rest;
216 }
217 if let Err(err) =
218 Ident::validate(component).map_err(PathError::InvalidComponent)
219 {
220 return Some(Err(err));
221 }
222 return Some(Ok(PathComponent::Normal(component)));
223 },
224 None if self.path.is_empty() => {
225 self.front = State::Done;
226 },
227 None => {
228 self.front = State::Done;
229 let component = self.path;
230 self.path = "";
231 if let Err(err) =
232 Ident::validate(component).map_err(PathError::InvalidComponent)
233 {
234 return Some(Err(err));
235 }
236 return Some(Ok(PathComponent::Normal(component)));
237 },
238 }
239 },
240 State::QuoteOpened => match self.path.split_once('"') {
241 Some(("", rest)) => {
242 self.path = rest;
243 self.front = State::QuoteClosed;
244 return Some(Err(PathError::EmptyComponent));
245 },
246 Some((quoted, rest)) => {
247 self.path = rest;
248 self.front = State::QuoteClosed;
249 return Some(Ok(PathComponent::Normal(quoted)));
250 },
251 None => {
252 self.front = State::Done;
253 return Some(Err(PathError::UnclosedQuotedComponent));
254 },
255 },
256 State::QuoteClosed => {
257 if self.path.is_empty() {
258 self.front = State::Done;
259 continue;
260 }
261 match self.path.strip_prefix("::") {
262 Some(rest) => {
263 self.path = rest;
264 self.front = State::Body;
265 },
266 None => {
267 self.front = State::Done;
268 return Some(Err(PathError::MissingPathSeparator));
269 },
270 }
271 },
272 State::Done => break,
273 }
274 }
275
276 None
277 }
278}
279
280impl<'a> DoubleEndedIterator for Components<'a> {
281 fn next_back(&mut self) -> Option<Self::Item> {
282 while !self.finished() {
283 match self.back {
284 State::Start => {
285 self.back = State::Done;
286 match self.path {
287 "" => break,
288 "::" => return Some(Ok(PathComponent::Root)),
289 other => {
290 assert!(
291 other.starts_with(Path::KERNEL_PATH)
292 || other.starts_with(Path::EXEC_PATH),
293 "expected path in start state to be a valid path prefix, got '{other}'"
294 );
295 return Some(Ok(PathComponent::Root));
296 },
297 }
298 },
299 State::Body => {
300 if let Some(rest) = self.path.strip_suffix('"') {
301 self.back = State::QuoteClosed;
302 self.path = rest;
303 continue;
304 }
305 match self.path.rsplit_once("::") {
306 Some(("", "")) => {
307 self.back = State::Start;
308 continue;
309 },
310 Some((prefix, component)) => {
311 if prefix.is_empty() {
312 self.path = "::";
313 self.back = State::Start;
314 } else {
315 self.path = prefix;
316 }
317 if let Err(err) =
318 Ident::validate(component).map_err(PathError::InvalidComponent)
319 {
320 return Some(Err(err));
321 }
322 return Some(Ok(PathComponent::Normal(component)));
323 },
324 None if self.path.is_empty() => {
325 self.back = State::Start;
326 },
327 None => {
328 self.back = State::Start;
329 let component = self.path;
330 if component.starts_with(Path::KERNEL_PATH)
331 || component.starts_with(Path::EXEC_PATH)
332 {
333 self.path = "::";
334 } else {
335 self.path = "";
336 }
337 if let Err(err) =
338 Ident::validate(component).map_err(PathError::InvalidComponent)
339 {
340 return Some(Err(err));
341 }
342 return Some(Ok(PathComponent::Normal(component)));
343 },
344 }
345 },
346 State::QuoteOpened => {
347 if self.path.is_empty() {
348 self.back = State::Start;
349 continue;
350 }
351 match self.path.strip_suffix("::") {
352 Some("") => {
353 self.back = State::Start;
354 },
355 Some(rest) => {
356 self.path = rest;
357 self.back = State::Body;
358 },
359 None => {
360 self.back = State::Done;
361 return Some(Err(PathError::MissingPathSeparator));
362 },
363 }
364 },
365 State::QuoteClosed => match self.path.rsplit_once('"') {
366 Some((rest, "")) => {
367 self.path = rest;
368 self.back = State::QuoteOpened;
369 return Some(Err(PathError::EmptyComponent));
370 },
371 Some((rest, quoted)) => {
372 self.path = rest;
373 self.back = State::QuoteOpened;
374 return Some(Ok(PathComponent::Normal(quoted)));
375 },
376 None => {
377 self.back = State::Done;
378 return Some(Err(PathError::UnclosedQuotedComponent));
379 },
380 },
381 State::Done => break,
382 }
383 }
384 None
385 }
386}
387
388#[cfg(test)]
389mod tests {
390 use miden_core::assert_matches;
391
392 use super::*;
393
394 #[test]
395 fn empty_path() {
396 let mut components = Iter::new("");
397 assert_matches!(components.next(), None);
398 }
399
400 #[test]
401 fn empty_path_back() {
402 let mut components = Iter::new("");
403 assert_matches!(components.next_back(), None);
404 }
405
406 #[test]
407 fn root_prefix_path() {
408 let mut components = Iter::new("::");
409 assert_matches!(components.next(), Some(Ok(PathComponent::Root)));
410 assert_matches!(components.next(), None);
411 }
412
413 #[test]
414 fn root_prefix_path_back() {
415 let mut components = Iter::new("::");
416 assert_matches!(components.next_back(), Some(Ok(PathComponent::Root)));
417 assert_matches!(components.next_back(), None);
418 }
419
420 #[test]
421 fn absolute_path() {
422 let mut components = Iter::new("::foo");
423 assert_matches!(components.next(), Some(Ok(PathComponent::Root)));
424 assert_matches!(components.next(), Some(Ok(PathComponent::Normal("foo"))));
425 assert_matches!(components.next(), None);
426 }
427
428 #[test]
429 fn absolute_path_back() {
430 let mut components = Iter::new("::foo");
431 assert_matches!(components.next_back(), Some(Ok(PathComponent::Normal("foo"))));
432 assert_matches!(components.next_back(), Some(Ok(PathComponent::Root)));
433 assert_matches!(components.next_back(), None);
434 }
435
436 #[test]
437 fn absolute_nested_path() {
438 let mut components = Iter::new("::foo::bar");
439 assert_matches!(components.next(), Some(Ok(PathComponent::Root)));
440 assert_matches!(components.next(), Some(Ok(PathComponent::Normal("foo"))));
441 assert_matches!(components.next(), Some(Ok(PathComponent::Normal("bar"))));
442 assert_matches!(components.next(), None);
443 }
444
445 #[test]
446 fn absolute_nested_path_back() {
447 let mut components = Iter::new("::foo::bar");
448 assert_matches!(components.next_back(), Some(Ok(PathComponent::Normal("bar"))));
449 assert_matches!(components.next_back(), Some(Ok(PathComponent::Normal("foo"))));
450 assert_matches!(components.next_back(), Some(Ok(PathComponent::Root)));
451 assert_matches!(components.next_back(), None);
452 }
453
454 #[test]
455 fn relative_path() {
456 let mut components = Iter::new("foo");
457 assert_matches!(components.next(), Some(Ok(PathComponent::Normal("foo"))));
458 assert_matches!(components.next(), None);
459 }
460
461 #[test]
462 fn relative_path_back() {
463 let mut components = Iter::new("foo");
464 assert_matches!(components.next_back(), Some(Ok(PathComponent::Normal("foo"))));
465 assert_matches!(components.next_back(), None);
466 }
467
468 #[test]
469 fn relative_nested_path() {
470 let mut components = Iter::new("foo::bar");
471 assert_matches!(components.next(), Some(Ok(PathComponent::Normal("foo"))));
472 assert_matches!(components.next(), Some(Ok(PathComponent::Normal("bar"))));
473 assert_matches!(components.next(), None);
474 }
475
476 #[test]
477 fn relative_nested_path_back() {
478 let mut components = Iter::new("foo::bar");
479 assert_matches!(components.next_back(), Some(Ok(PathComponent::Normal("bar"))));
480 assert_matches!(components.next_back(), Some(Ok(PathComponent::Normal("foo"))));
481 assert_matches!(components.next_back(), None);
482 }
483
484 #[test]
485 fn special_path() {
486 let mut components = Iter::new("$kernel");
487 assert_matches!(components.next(), Some(Ok(PathComponent::Root)));
488 assert_matches!(components.next(), Some(Ok(PathComponent::Normal("$kernel"))));
489 assert_matches!(components.next(), None);
490 }
491
492 #[test]
493 fn special_path_back() {
494 let mut components = Iter::new("$kernel");
495 assert_matches!(components.next_back(), Some(Ok(PathComponent::Normal("$kernel"))));
496 assert_matches!(components.next_back(), Some(Ok(PathComponent::Root)));
497 assert_matches!(components.next_back(), None);
498 }
499
500 #[test]
501 fn special_nested_path() {
502 let mut components = Iter::new("$kernel::bar");
503 assert_matches!(components.next(), Some(Ok(PathComponent::Root)));
504 assert_matches!(components.next(), Some(Ok(PathComponent::Normal("$kernel"))));
505 assert_matches!(components.next(), Some(Ok(PathComponent::Normal("bar"))));
506 assert_matches!(components.next(), None);
507 }
508
509 #[test]
510 fn special_nested_path_back() {
511 let mut components = Iter::new("$kernel::bar");
512 assert_matches!(components.next_back(), Some(Ok(PathComponent::Normal("bar"))));
513 assert_matches!(components.next_back(), Some(Ok(PathComponent::Normal("$kernel"))));
514 assert_matches!(components.next_back(), Some(Ok(PathComponent::Root)));
515 assert_matches!(components.next_back(), None);
516 }
517
518 #[test]
519 fn path_with_quoted_component() {
520 let mut components = Iter::new("\"foo\"");
521 assert_matches!(components.next(), Some(Ok(PathComponent::Normal("foo"))));
522 assert_matches!(components.next(), None);
523 }
524
525 #[test]
526 fn path_with_quoted_component_back() {
527 let mut components = Iter::new("\"foo\"");
528 assert_matches!(components.next_back(), Some(Ok(PathComponent::Normal("foo"))));
529 assert_matches!(components.next_back(), None);
530 }
531
532 #[test]
533 fn nested_path_with_quoted_component() {
534 let mut components = Iter::new("foo::\"bar\"");
535 assert_matches!(components.next(), Some(Ok(PathComponent::Normal("foo"))));
536 assert_matches!(components.next(), Some(Ok(PathComponent::Normal("bar"))));
537 assert_matches!(components.next(), None);
538 }
539
540 #[test]
541 fn nested_path_with_quoted_component_back() {
542 let mut components = Iter::new("foo::\"bar\"");
543 assert_matches!(components.next_back(), Some(Ok(PathComponent::Normal("bar"))));
544 assert_matches!(components.next_back(), Some(Ok(PathComponent::Normal("foo"))));
545 assert_matches!(components.next_back(), None);
546 }
547
548 #[test]
549 fn nested_path_with_interspersed_quoted_component() {
550 let mut components = Iter::new("foo::\"bar\"::baz");
551 assert_matches!(components.next(), Some(Ok(PathComponent::Normal("foo"))));
552 assert_matches!(components.next(), Some(Ok(PathComponent::Normal("bar"))));
553 assert_matches!(components.next(), Some(Ok(PathComponent::Normal("baz"))));
554 assert_matches!(components.next(), None);
555 }
556
557 #[test]
558 fn nested_path_with_interspersed_quoted_component_back() {
559 let mut components = Iter::new("foo::\"bar\"::baz");
560 assert_matches!(components.next_back(), Some(Ok(PathComponent::Normal("baz"))));
561 assert_matches!(components.next_back(), Some(Ok(PathComponent::Normal("bar"))));
562 assert_matches!(components.next_back(), Some(Ok(PathComponent::Normal("foo"))));
563 assert_matches!(components.next_back(), None);
564 }
565}