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 },
366 Some((prefix, component)) => {
367 self.back_pos -= component.len() + 2;
368 if prefix.is_empty() {
369 self.path = "::";
370 self.back = State::Start;
371 } else {
372 self.path = prefix;
373 }
374 if let Err(err) =
375 Ident::validate(component).map_err(PathError::InvalidComponent)
376 {
377 return Some(Err(err));
378 }
379 return Some(Ok(PathComponent::Normal(component)));
380 },
381 None if self.path.is_empty() => {
382 self.back = State::Start;
383 },
384 None => {
385 self.back = State::Start;
386 let component = self.path;
387 self.path = "";
388 self.back_pos = 0;
389 if let Err(err) =
390 Ident::validate(component).map_err(PathError::InvalidComponent)
391 {
392 return Some(Err(err));
393 }
394 return Some(Ok(PathComponent::Normal(component)));
395 },
396 }
397 },
398 State::QuoteOpened(_) => {
399 if self.path.is_empty() {
400 self.back = State::Start;
401 } else {
402 match self.path.strip_suffix("::") {
403 Some("") => {
404 self.back = State::Start;
405 self.back_pos -= 2;
406 },
407 Some(rest) => {
408 self.back_pos -= 2;
409 self.path = rest;
410 self.back = State::Body;
411 },
412 None if quote_closed.is_some() => (),
416 None => {
417 self.back = State::Done;
418 return Some(Err(PathError::MissingPathSeparator));
419 },
420 }
421 }
422
423 if quote_closed.is_some() {
424 return quote_closed;
425 }
426 },
427 State::QuoteClosed(closed_at) => match self.path.rsplit_once('"') {
428 Some((rest, "")) => {
429 self.back_pos -= 1;
430 self.path = rest;
431 self.back = State::QuoteOpened(self.back_pos);
432 quote_closed = Some(Err(PathError::EmptyComponent));
433 },
434 Some((rest, quoted)) => {
435 self.back_pos -= quoted.len() + 1;
436 let quoted = &self.original[self.back_pos..closed_at];
437 self.path = rest;
438 self.back = State::QuoteOpened(self.back_pos);
439 quote_closed = Some(Ok(PathComponent::Normal(quoted)));
440 },
441 None => {
442 self.back = State::Done;
443 self.back_pos = 0;
444 return Some(Err(PathError::UnclosedQuotedComponent));
445 },
446 },
447 State::Done => break,
448 }
449 }
450 None
451 }
452}
453
454#[cfg(test)]
455mod tests {
456 use miden_core::assert_matches;
457
458 use super::*;
459
460 #[test]
461 fn empty_path() {
462 let mut components = Iter::new("");
463 assert_matches!(components.next(), None);
464 }
465
466 #[test]
467 fn empty_path_back() {
468 let mut components = Iter::new("");
469 assert_matches!(components.next_back(), None);
470 }
471
472 #[test]
473 fn root_prefix_path() {
474 let mut components = Iter::new("::");
475 assert_matches!(components.next(), Some(Ok(PathComponent::Root)));
476 assert_matches!(components.next(), None);
477 }
478
479 #[test]
480 fn root_prefix_path_back() {
481 let mut components = Iter::new("::");
482 assert_matches!(components.next_back(), Some(Ok(PathComponent::Root)));
483 assert_matches!(components.next_back(), None);
484 }
485
486 #[test]
487 fn absolute_path() {
488 let mut components = Iter::new("::foo");
489 assert_matches!(components.next(), Some(Ok(PathComponent::Root)));
490 assert_matches!(components.next(), Some(Ok(PathComponent::Normal("foo"))));
491 assert_matches!(components.next(), None);
492 }
493
494 #[test]
495 fn absolute_path_back() {
496 let mut components = Iter::new("::foo");
497 assert_matches!(components.next_back(), Some(Ok(PathComponent::Normal("foo"))));
498 assert_matches!(components.next_back(), Some(Ok(PathComponent::Root)));
499 assert_matches!(components.next_back(), None);
500 }
501
502 #[test]
503 fn absolute_nested_path() {
504 let mut components = Iter::new("::foo::bar");
505 assert_matches!(components.next(), Some(Ok(PathComponent::Root)));
506 assert_matches!(components.next(), Some(Ok(PathComponent::Normal("foo"))));
507 assert_matches!(components.next(), Some(Ok(PathComponent::Normal("bar"))));
508 assert_matches!(components.next(), None);
509 }
510
511 #[test]
512 fn absolute_nested_path_back() {
513 let mut components = Iter::new("::foo::bar");
514 assert_matches!(components.next_back(), Some(Ok(PathComponent::Normal("bar"))));
515 assert_matches!(components.next_back(), Some(Ok(PathComponent::Normal("foo"))));
516 assert_matches!(components.next_back(), Some(Ok(PathComponent::Root)));
517 assert_matches!(components.next_back(), None);
518 }
519
520 #[test]
521 fn relative_path() {
522 let mut components = Iter::new("foo");
523 assert_matches!(components.next(), Some(Ok(PathComponent::Normal("foo"))));
524 assert_matches!(components.next(), None);
525 }
526
527 #[test]
528 fn relative_path_back() {
529 let mut components = Iter::new("foo");
530 assert_matches!(components.next_back(), Some(Ok(PathComponent::Normal("foo"))));
531 assert_matches!(components.next_back(), None);
532 }
533
534 #[test]
535 fn relative_nested_path() {
536 let mut components = Iter::new("foo::bar");
537 assert_matches!(components.next(), Some(Ok(PathComponent::Normal("foo"))));
538 assert_matches!(components.next(), Some(Ok(PathComponent::Normal("bar"))));
539 assert_matches!(components.next(), None);
540 }
541
542 #[test]
543 fn relative_nested_path_back() {
544 let mut components = Iter::new("foo::bar");
545 assert_matches!(components.next_back(), Some(Ok(PathComponent::Normal("bar"))));
546 assert_matches!(components.next_back(), Some(Ok(PathComponent::Normal("foo"))));
547 assert_matches!(components.next_back(), None);
548 }
549
550 #[test]
551 fn special_path() {
552 let mut components = Iter::new("$kernel");
553 assert_matches!(components.next(), Some(Ok(PathComponent::Normal("$kernel"))));
554 assert_matches!(components.next(), None);
555
556 let mut components = Iter::new("::$kernel");
557 assert_matches!(components.next(), Some(Ok(PathComponent::Root)));
558 assert_matches!(components.next(), Some(Ok(PathComponent::Normal("$kernel"))));
559 assert_matches!(components.next(), None);
560 }
561
562 #[test]
563 fn special_path_back() {
564 let mut components = Iter::new("$kernel");
565 assert_matches!(components.next_back(), Some(Ok(PathComponent::Normal("$kernel"))));
566 assert_matches!(components.next_back(), None);
567
568 let mut components = Iter::new("::$kernel");
569 assert_matches!(components.next_back(), Some(Ok(PathComponent::Normal("$kernel"))));
570 assert_matches!(components.next_back(), Some(Ok(PathComponent::Root)));
571 assert_matches!(components.next_back(), None);
572 }
573
574 #[test]
575 fn special_nested_path() {
576 let mut components = Iter::new("$kernel::bar");
577 assert_matches!(components.next(), Some(Ok(PathComponent::Normal("$kernel"))));
578 assert_matches!(components.next(), Some(Ok(PathComponent::Normal("bar"))));
579 assert_matches!(components.next(), None);
580
581 let mut components = Iter::new("::$kernel::bar");
582 assert_matches!(components.next(), Some(Ok(PathComponent::Root)));
583 assert_matches!(components.next(), Some(Ok(PathComponent::Normal("$kernel"))));
584 assert_matches!(components.next(), Some(Ok(PathComponent::Normal("bar"))));
585 assert_matches!(components.next(), None);
586 }
587
588 #[test]
589 fn special_nested_path_back() {
590 let mut components = Iter::new("$kernel::bar");
591 assert_matches!(components.next_back(), Some(Ok(PathComponent::Normal("bar"))));
592 assert_matches!(components.next_back(), Some(Ok(PathComponent::Normal("$kernel"))));
593 assert_matches!(components.next_back(), None);
594
595 let mut components = Iter::new("::$kernel::bar");
596 assert_matches!(components.next_back(), Some(Ok(PathComponent::Normal("bar"))));
597 assert_matches!(components.next_back(), Some(Ok(PathComponent::Normal("$kernel"))));
598 assert_matches!(components.next_back(), Some(Ok(PathComponent::Root)));
599 assert_matches!(components.next_back(), None);
600 }
601
602 #[test]
603 fn path_with_quoted_component() {
604 let mut components = Iter::new("\"foo\"");
605 assert_matches!(components.next(), Some(Ok(PathComponent::Normal("\"foo\""))));
606 assert_matches!(components.next(), None);
607 }
608
609 #[test]
610 fn path_with_quoted_component_back() {
611 let mut components = Iter::new("\"foo\"");
612 assert_matches!(components.next_back(), Some(Ok(PathComponent::Normal("\"foo\""))));
613 assert_matches!(components.next_back(), None);
614 }
615
616 #[test]
617 fn nested_path_with_quoted_component() {
618 let mut components = Iter::new("foo::\"bar\"");
619 assert_matches!(components.next(), Some(Ok(PathComponent::Normal("foo"))));
620 assert_matches!(components.next(), Some(Ok(PathComponent::Normal("\"bar\""))));
621 assert_matches!(components.next(), None);
622 }
623
624 #[test]
625 fn nested_path_with_quoted_component_back() {
626 let mut components = Iter::new("foo::\"bar\"");
627 assert_matches!(components.next_back(), Some(Ok(PathComponent::Normal("\"bar\""))));
628 assert_matches!(components.next_back(), Some(Ok(PathComponent::Normal("foo"))));
629 assert_matches!(components.next_back(), None);
630 }
631
632 #[test]
633 fn nested_path_with_interspersed_quoted_component() {
634 let mut components = Iter::new("foo::\"bar\"::baz");
635 assert_matches!(components.next(), Some(Ok(PathComponent::Normal("foo"))));
636 assert_matches!(components.next(), Some(Ok(PathComponent::Normal("\"bar\""))));
637 assert_matches!(components.next(), Some(Ok(PathComponent::Normal("baz"))));
638 assert_matches!(components.next(), None);
639 }
640
641 #[test]
642 fn nested_path_with_interspersed_quoted_component_back() {
643 let mut components = Iter::new("foo::\"bar\"::baz");
644 assert_matches!(components.next_back(), Some(Ok(PathComponent::Normal("baz"))));
645 assert_matches!(components.next_back(), Some(Ok(PathComponent::Normal("\"bar\""))));
646 assert_matches!(components.next_back(), Some(Ok(PathComponent::Normal("foo"))));
647 assert_matches!(components.next_back(), None);
648 }
649}