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 pub fn as_str(&self) -> &'a str {
30 match self {
31 Self::Root => "::",
32 Self::Normal(id) if id.starts_with('"') && id.ends_with('"') => &id[1..(id.len() - 1)],
33 Self::Normal(id) => id,
34 }
35 }
36
37 #[inline]
39 pub fn to_ident(&self) -> Option<Ident> {
40 if matches!(self, Self::Root) {
41 None
42 } else {
43 Some(Ident::from_raw_parts(Span::unknown(Arc::from(
44 self.as_str().to_string().into_boxed_str(),
45 ))))
46 }
47 }
48
49 pub fn char_len(&self) -> usize {
51 self.as_str().chars().count()
52 }
53
54 pub fn is_quoted(&self) -> bool {
56 matches!(self, Self::Normal(component) if component.starts_with('"') && component.ends_with('"'))
57 }
58
59 pub fn requires_quoting(&self) -> bool {
61 match self {
62 Self::Root => false,
63 Self::Normal(Path::KERNEL_PATH | Path::EXEC_PATH) => false,
64 Self::Normal(component)
65 if component.contains("::") || Ident::requires_quoting(component) =>
66 {
67 true
68 },
69 Self::Normal(_) => false,
70 }
71 }
72}
73
74impl PartialEq<str> for PathComponent<'_> {
75 fn eq(&self, other: &str) -> bool {
76 self.as_str().eq(other)
77 }
78}
79
80impl AsRef<str> for PathComponent<'_> {
81 #[inline(always)]
82 fn as_ref(&self) -> &str {
83 self.as_str()
84 }
85}
86
87impl fmt::Display for PathComponent<'_> {
88 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
89 f.write_str(self.as_str())
90 }
91}
92
93#[derive(Debug)]
115pub struct Iter<'a> {
116 components: Components<'a>,
117}
118
119impl<'a> Iter<'a> {
120 pub fn new(path: &'a str) -> Self {
121 Self {
122 components: Components {
123 path,
124 original: path,
125 front_pos: 0,
126 front: State::Start,
127 back_pos: path.len(),
128 back: State::Body,
129 },
130 }
131 }
132
133 #[inline]
134 pub fn as_path(&self) -> &'a Path {
135 Path::new(self.components.path)
136 }
137}
138
139impl FusedIterator for Iter<'_> {}
140
141impl<'a> Iterator for Iter<'a> {
142 type Item = Result<PathComponent<'a>, PathError>;
143
144 fn next(&mut self) -> Option<Self::Item> {
145 match self.components.next() {
146 Some(Ok(component @ PathComponent::Normal(_)))
147 if component.as_str().chars().count() > Path::MAX_COMPONENT_LENGTH =>
148 {
149 Some(Err(PathError::InvalidComponent(crate::ast::IdentError::InvalidLength {
150 max: Path::MAX_COMPONENT_LENGTH,
151 })))
152 },
153 next => next,
154 }
155 }
156}
157
158impl<'a> DoubleEndedIterator for Iter<'a> {
159 fn next_back(&mut self) -> Option<Self::Item> {
160 match self.components.next_back() {
161 Some(Ok(component @ PathComponent::Normal(_)))
162 if component.as_str().chars().count() > Path::MAX_COMPONENT_LENGTH =>
163 {
164 Some(Err(PathError::InvalidComponent(crate::ast::IdentError::InvalidLength {
165 max: Path::MAX_COMPONENT_LENGTH,
166 })))
167 },
168 next => next,
169 }
170 }
171}
172
173#[derive(Debug)]
175struct Components<'a> {
176 original: &'a str,
177 path: &'a str,
179 front_pos: usize,
182 front: State,
183 back_pos: usize,
184 back: State,
185}
186
187#[derive(Debug, Copy, Clone, PartialEq)]
188enum State {
189 Start,
191 Body,
193 QuoteOpened(usize),
195 QuoteClosed(usize),
197 Done,
199}
200
201impl<'a> Components<'a> {
202 fn finished(&self) -> bool {
203 match (self.front, self.back) {
204 (State::Done, _) => true,
205 (_, State::Done) => true,
206 (State::Body | State::QuoteOpened(_) | State::QuoteClosed(_), State::Start) => true,
207 (..) => false,
208 }
209 }
210}
211
212impl<'a> Iterator for Components<'a> {
213 type Item = Result<PathComponent<'a>, PathError>;
214
215 fn next(&mut self) -> Option<Self::Item> {
216 let mut quote_opened = None;
220 while !self.finished() || quote_opened.is_some() {
221 match self.front {
222 State::Start => match self.path.strip_prefix("::") {
223 Some(rest) => {
224 self.path = rest;
225 self.front = State::Body;
226 self.front_pos += 2;
227 return Some(Ok(PathComponent::Root));
228 },
229 None => {
230 self.front = State::Body;
231 },
232 },
233 State::Body => {
234 if let Some(rest) = self.path.strip_prefix('"') {
235 self.front = State::QuoteOpened(self.front_pos);
236 self.front_pos += 1;
237 self.path = rest;
238 continue;
239 }
240 match self.path.split_once("::") {
241 Some(("", rest)) => {
242 self.path = rest;
243 self.front_pos += 2;
244 return Some(Err(PathError::InvalidComponent(
245 crate::ast::IdentError::Empty,
246 )));
247 },
248 Some((component, rest)) => {
249 self.front_pos += component.len() + 2;
250 if rest.is_empty() {
251 self.path = "::";
252 } else {
253 self.path = rest;
254 }
255 if let Err(err) =
256 Ident::validate(component).map_err(PathError::InvalidComponent)
257 {
258 return Some(Err(err));
259 }
260 return Some(Ok(PathComponent::Normal(component)));
261 },
262 None if self.path.is_empty() => {
263 self.front = State::Done;
264 },
265 None => {
266 self.front = State::Done;
267 let component = self.path;
268 self.path = "";
269 if let Err(err) =
270 Ident::validate(component).map_err(PathError::InvalidComponent)
271 {
272 return Some(Err(err));
273 }
274 self.front_pos += component.len();
275 return Some(Ok(PathComponent::Normal(component)));
276 },
277 }
278 },
279 State::QuoteOpened(opened_at) => match self.path.split_once('"') {
280 Some(("", rest)) => {
281 self.path = rest;
282 self.front = State::QuoteClosed(self.front_pos);
283 self.front_pos += 1;
284 quote_opened = Some(Err(PathError::EmptyComponent));
285 },
286 Some((quoted, rest)) => {
287 self.path = rest;
288 self.front_pos += quoted.len();
289 self.front = State::QuoteClosed(self.front_pos);
290 self.front_pos += 1;
291 let quoted = &self.original[opened_at..self.front_pos];
292 quote_opened = Some(Ok(PathComponent::Normal(quoted)));
293 },
294 None => {
295 self.front = State::Done;
296 self.front_pos += self.path.len();
297 return Some(Err(PathError::UnclosedQuotedComponent));
298 },
299 },
300 State::QuoteClosed(_) => {
301 if self.path.is_empty() {
302 self.front = State::Done;
303 } else {
304 match self.path.strip_prefix("::") {
305 Some(rest) => {
306 self.path = rest;
307 self.front = State::Body;
308 self.front_pos += 2;
309 },
310 None if quote_opened.is_some() => (),
314 None => {
315 self.front = State::Done;
316 return Some(Err(PathError::MissingPathSeparator));
317 },
318 }
319 }
320
321 if quote_opened.is_some() {
322 return quote_opened;
323 }
324 },
325 State::Done => break,
326 }
327 }
328
329 None
330 }
331}
332
333impl<'a> DoubleEndedIterator for Components<'a> {
334 fn next_back(&mut self) -> Option<Self::Item> {
335 let mut quote_closed = None;
339 while !self.finished() || quote_closed.is_some() {
340 match self.back {
341 State::Start => {
342 self.back = State::Done;
343 match self.path {
344 "" => break,
345 "::" => {
346 self.back_pos = 0;
347 return Some(Ok(PathComponent::Root));
348 },
349 other => {
350 return Some(Ok(PathComponent::Normal(other)));
351 },
352 }
353 },
354 State::Body => {
355 if let Some(rest) = self.path.strip_suffix('"') {
356 self.back = State::QuoteClosed(self.back_pos);
357 self.back_pos -= 1;
358 self.path = rest;
359 continue;
360 }
361 match self.path.rsplit_once("::") {
362 Some(("", "")) => {
363 self.back = State::Start;
364 self.back_pos -= 2;
365 continue;
366 },
367 Some((prefix, component)) => {
368 self.back_pos -= component.len() + 2;
369 if prefix.is_empty() {
370 self.path = "::";
371 self.back = State::Start;
372 } else {
373 self.path = prefix;
374 }
375 if let Err(err) =
376 Ident::validate(component).map_err(PathError::InvalidComponent)
377 {
378 return Some(Err(err));
379 }
380 return Some(Ok(PathComponent::Normal(component)));
381 },
382 None if self.path.is_empty() => {
383 self.back = State::Start;
384 },
385 None => {
386 self.back = State::Start;
387 let component = self.path;
388 self.path = "";
389 self.back_pos = 0;
390 if let Err(err) =
391 Ident::validate(component).map_err(PathError::InvalidComponent)
392 {
393 return Some(Err(err));
394 }
395 return Some(Ok(PathComponent::Normal(component)));
396 },
397 }
398 },
399 State::QuoteOpened(_) => {
400 if self.path.is_empty() {
401 self.back = State::Start;
402 } else {
403 match self.path.strip_suffix("::") {
404 Some("") => {
405 self.back = State::Start;
406 self.back_pos -= 2;
407 },
408 Some(rest) => {
409 self.back_pos -= 2;
410 self.path = rest;
411 self.back = State::Body;
412 },
413 None if quote_closed.is_some() => (),
417 None => {
418 self.back = State::Done;
419 return Some(Err(PathError::MissingPathSeparator));
420 },
421 }
422 }
423
424 if quote_closed.is_some() {
425 return quote_closed;
426 }
427 },
428 State::QuoteClosed(closed_at) => match self.path.rsplit_once('"') {
429 Some((rest, "")) => {
430 self.back_pos -= 1;
431 self.path = rest;
432 self.back = State::QuoteOpened(self.back_pos);
433 quote_closed = Some(Err(PathError::EmptyComponent));
434 },
435 Some((rest, quoted)) => {
436 self.back_pos -= quoted.len() + 1;
437 let quoted = &self.original[self.back_pos..closed_at];
438 self.path = rest;
439 self.back = State::QuoteOpened(self.back_pos);
440 quote_closed = Some(Ok(PathComponent::Normal(quoted)));
441 },
442 None => {
443 self.back = State::Done;
444 self.back_pos = 0;
445 return Some(Err(PathError::UnclosedQuotedComponent));
446 },
447 },
448 State::Done => break,
449 }
450 }
451 None
452 }
453}
454
455#[cfg(test)]
456mod tests {
457 use miden_core::assert_matches;
458
459 use super::*;
460
461 #[test]
462 fn empty_path() {
463 let mut components = Iter::new("");
464 assert_matches!(components.next(), None);
465 }
466
467 #[test]
468 fn empty_path_back() {
469 let mut components = Iter::new("");
470 assert_matches!(components.next_back(), None);
471 }
472
473 #[test]
474 fn root_prefix_path() {
475 let mut components = Iter::new("::");
476 assert_matches!(components.next(), Some(Ok(PathComponent::Root)));
477 assert_matches!(components.next(), None);
478 }
479
480 #[test]
481 fn root_prefix_path_back() {
482 let mut components = Iter::new("::");
483 assert_matches!(components.next_back(), Some(Ok(PathComponent::Root)));
484 assert_matches!(components.next_back(), None);
485 }
486
487 #[test]
488 fn absolute_path() {
489 let mut components = Iter::new("::foo");
490 assert_matches!(components.next(), Some(Ok(PathComponent::Root)));
491 assert_matches!(components.next(), Some(Ok(PathComponent::Normal("foo"))));
492 assert_matches!(components.next(), None);
493 }
494
495 #[test]
496 fn absolute_path_back() {
497 let mut components = Iter::new("::foo");
498 assert_matches!(components.next_back(), Some(Ok(PathComponent::Normal("foo"))));
499 assert_matches!(components.next_back(), Some(Ok(PathComponent::Root)));
500 assert_matches!(components.next_back(), None);
501 }
502
503 #[test]
504 fn absolute_nested_path() {
505 let mut components = Iter::new("::foo::bar");
506 assert_matches!(components.next(), Some(Ok(PathComponent::Root)));
507 assert_matches!(components.next(), Some(Ok(PathComponent::Normal("foo"))));
508 assert_matches!(components.next(), Some(Ok(PathComponent::Normal("bar"))));
509 assert_matches!(components.next(), None);
510 }
511
512 #[test]
513 fn absolute_nested_path_back() {
514 let mut components = Iter::new("::foo::bar");
515 assert_matches!(components.next_back(), Some(Ok(PathComponent::Normal("bar"))));
516 assert_matches!(components.next_back(), Some(Ok(PathComponent::Normal("foo"))));
517 assert_matches!(components.next_back(), Some(Ok(PathComponent::Root)));
518 assert_matches!(components.next_back(), None);
519 }
520
521 #[test]
522 fn relative_path() {
523 let mut components = Iter::new("foo");
524 assert_matches!(components.next(), Some(Ok(PathComponent::Normal("foo"))));
525 assert_matches!(components.next(), None);
526 }
527
528 #[test]
529 fn relative_path_back() {
530 let mut components = Iter::new("foo");
531 assert_matches!(components.next_back(), Some(Ok(PathComponent::Normal("foo"))));
532 assert_matches!(components.next_back(), None);
533 }
534
535 #[test]
536 fn relative_nested_path() {
537 let mut components = Iter::new("foo::bar");
538 assert_matches!(components.next(), Some(Ok(PathComponent::Normal("foo"))));
539 assert_matches!(components.next(), Some(Ok(PathComponent::Normal("bar"))));
540 assert_matches!(components.next(), None);
541 }
542
543 #[test]
544 fn relative_nested_path_back() {
545 let mut components = Iter::new("foo::bar");
546 assert_matches!(components.next_back(), Some(Ok(PathComponent::Normal("bar"))));
547 assert_matches!(components.next_back(), Some(Ok(PathComponent::Normal("foo"))));
548 assert_matches!(components.next_back(), None);
549 }
550
551 #[test]
552 fn special_path() {
553 let mut components = Iter::new("$kernel");
554 assert_matches!(components.next(), Some(Ok(PathComponent::Normal("$kernel"))));
555 assert_matches!(components.next(), None);
556
557 let mut components = Iter::new("::$kernel");
558 assert_matches!(components.next(), Some(Ok(PathComponent::Root)));
559 assert_matches!(components.next(), Some(Ok(PathComponent::Normal("$kernel"))));
560 assert_matches!(components.next(), None);
561 }
562
563 #[test]
564 fn special_path_back() {
565 let mut components = Iter::new("$kernel");
566 assert_matches!(components.next_back(), Some(Ok(PathComponent::Normal("$kernel"))));
567 assert_matches!(components.next_back(), None);
568
569 let mut components = Iter::new("::$kernel");
570 assert_matches!(components.next_back(), Some(Ok(PathComponent::Normal("$kernel"))));
571 assert_matches!(components.next_back(), Some(Ok(PathComponent::Root)));
572 assert_matches!(components.next_back(), None);
573 }
574
575 #[test]
576 fn special_nested_path() {
577 let mut components = Iter::new("$kernel::bar");
578 assert_matches!(components.next(), Some(Ok(PathComponent::Normal("$kernel"))));
579 assert_matches!(components.next(), Some(Ok(PathComponent::Normal("bar"))));
580 assert_matches!(components.next(), None);
581
582 let mut components = Iter::new("::$kernel::bar");
583 assert_matches!(components.next(), Some(Ok(PathComponent::Root)));
584 assert_matches!(components.next(), Some(Ok(PathComponent::Normal("$kernel"))));
585 assert_matches!(components.next(), Some(Ok(PathComponent::Normal("bar"))));
586 assert_matches!(components.next(), None);
587 }
588
589 #[test]
590 fn special_nested_path_back() {
591 let mut components = Iter::new("$kernel::bar");
592 assert_matches!(components.next_back(), Some(Ok(PathComponent::Normal("bar"))));
593 assert_matches!(components.next_back(), Some(Ok(PathComponent::Normal("$kernel"))));
594 assert_matches!(components.next_back(), None);
595
596 let mut components = Iter::new("::$kernel::bar");
597 assert_matches!(components.next_back(), Some(Ok(PathComponent::Normal("bar"))));
598 assert_matches!(components.next_back(), Some(Ok(PathComponent::Normal("$kernel"))));
599 assert_matches!(components.next_back(), Some(Ok(PathComponent::Root)));
600 assert_matches!(components.next_back(), None);
601 }
602
603 #[test]
604 fn path_with_quoted_component() {
605 let mut components = Iter::new("\"foo\"");
606 assert_matches!(components.next(), Some(Ok(PathComponent::Normal("\"foo\""))));
607 assert_matches!(components.next(), None);
608 }
609
610 #[test]
611 fn path_with_quoted_component_back() {
612 let mut components = Iter::new("\"foo\"");
613 assert_matches!(components.next_back(), Some(Ok(PathComponent::Normal("\"foo\""))));
614 assert_matches!(components.next_back(), None);
615 }
616
617 #[test]
618 fn nested_path_with_quoted_component() {
619 let mut components = Iter::new("foo::\"bar\"");
620 assert_matches!(components.next(), Some(Ok(PathComponent::Normal("foo"))));
621 assert_matches!(components.next(), Some(Ok(PathComponent::Normal("\"bar\""))));
622 assert_matches!(components.next(), None);
623 }
624
625 #[test]
626 fn nested_path_with_quoted_component_back() {
627 let mut components = Iter::new("foo::\"bar\"");
628 assert_matches!(components.next_back(), Some(Ok(PathComponent::Normal("\"bar\""))));
629 assert_matches!(components.next_back(), Some(Ok(PathComponent::Normal("foo"))));
630 assert_matches!(components.next_back(), None);
631 }
632
633 #[test]
634 fn nested_path_with_interspersed_quoted_component() {
635 let mut components = Iter::new("foo::\"bar\"::baz");
636 assert_matches!(components.next(), Some(Ok(PathComponent::Normal("foo"))));
637 assert_matches!(components.next(), Some(Ok(PathComponent::Normal("\"bar\""))));
638 assert_matches!(components.next(), Some(Ok(PathComponent::Normal("baz"))));
639 assert_matches!(components.next(), None);
640 }
641
642 #[test]
643 fn nested_path_with_interspersed_quoted_component_back() {
644 let mut components = Iter::new("foo::\"bar\"::baz");
645 assert_matches!(components.next_back(), Some(Ok(PathComponent::Normal("baz"))));
646 assert_matches!(components.next_back(), Some(Ok(PathComponent::Normal("\"bar\""))));
647 assert_matches!(components.next_back(), Some(Ok(PathComponent::Normal("foo"))));
648 assert_matches!(components.next_back(), None);
649 }
650}