miden_assembly_syntax/ast/path/
path.rs1use alloc::{
2 borrow::{Borrow, Cow, ToOwned},
3 string::ToString,
4};
5use core::fmt;
6
7use super::{Iter, Join, PathBuf, PathComponent, PathError, StartsWith};
8use crate::ast::Ident;
9
10#[derive(PartialEq, Eq, PartialOrd, Ord, Hash)]
12#[repr(transparent)]
13pub struct Path {
14 inner: str,
16}
17
18impl fmt::Debug for Path {
19 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
20 fmt::Debug::fmt(&self.inner, f)
21 }
22}
23
24#[cfg(feature = "serde")]
25impl serde::Serialize for Path {
26 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
27 where
28 S: serde::Serializer,
29 {
30 serializer.serialize_str(&self.inner)
31 }
32}
33
34#[cfg(feature = "serde")]
35impl<'de> serde::Deserialize<'de> for &'de Path {
36 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
37 where
38 D: serde::Deserializer<'de>,
39 {
40 use serde::de::Visitor;
41
42 struct PathVisitor;
43
44 impl<'de> Visitor<'de> for PathVisitor {
45 type Value = &'de Path;
46
47 fn expecting(&self, formatter: &mut core::fmt::Formatter) -> core::fmt::Result {
48 formatter.write_str("a borrowed Path")
49 }
50
51 fn visit_borrowed_str<E>(self, v: &'de str) -> Result<Self::Value, E>
52 where
53 E: serde::de::Error,
54 {
55 Path::validate(v).map_err(serde::de::Error::custom)
56 }
57 }
58
59 deserializer.deserialize_any(PathVisitor)
60 }
61}
62
63impl ToOwned for Path {
64 type Owned = PathBuf;
65 #[inline]
66 fn to_owned(&self) -> PathBuf {
67 self.to_path_buf()
68 }
69 #[inline]
70 fn clone_into(&self, target: &mut Self::Owned) {
71 self.inner.clone_into(&mut target.inner)
72 }
73}
74
75impl Borrow<Path> for PathBuf {
76 fn borrow(&self) -> &Path {
77 Path::new(self)
78 }
79}
80
81impl AsRef<str> for Path {
82 #[inline]
83 fn as_ref(&self) -> &str {
84 &self.inner
85 }
86}
87
88impl AsRef<Path> for str {
89 #[inline(always)]
90 fn as_ref(&self) -> &Path {
91 unsafe { &*(self as *const str as *const Path) }
92 }
93}
94
95impl AsRef<Path> for Ident {
96 #[inline(always)]
97 fn as_ref(&self) -> &Path {
98 self.as_str().as_ref()
99 }
100}
101
102impl AsRef<Path> for crate::ast::ProcedureName {
103 #[inline(always)]
104 fn as_ref(&self) -> &Path {
105 let ident: &Ident = self.as_ref();
106 ident.as_str().as_ref()
107 }
108}
109
110impl AsRef<Path> for crate::ast::QualifiedProcedureName {
111 #[inline(always)]
112 fn as_ref(&self) -> &Path {
113 self.as_path()
114 }
115}
116
117impl AsRef<Path> for Path {
118 #[inline(always)]
119 fn as_ref(&self) -> &Path {
120 self
121 }
122}
123
124impl From<&Path> for alloc::sync::Arc<Path> {
125 fn from(path: &Path) -> Self {
126 path.to_path_buf().into()
127 }
128}
129
130impl Path {
132 pub const MAX_COMPONENT_LENGTH: usize = u8::MAX as usize;
134
135 pub const EMPTY: &Path = unsafe { &*("" as *const str as *const Path) };
137
138 pub const KERNEL_PATH: &str = "$kernel";
140 pub const ABSOLUTE_KERNEL_PATH: &str = "::$kernel";
141 pub const KERNEL: &Path =
142 unsafe { &*(Self::ABSOLUTE_KERNEL_PATH as *const str as *const Path) };
143
144 pub const EXEC_PATH: &str = "$exec";
146 pub const ABSOLUTE_EXEC_PATH: &str = "::$exec";
147 pub const EXEC: &Path = unsafe { &*(Self::ABSOLUTE_EXEC_PATH as *const str as *const Path) };
148
149 pub fn new<S: AsRef<str> + ?Sized>(path: &S) -> &Path {
150 unsafe { &*(path.as_ref() as *const str as *const Path) }
152 }
153
154 pub fn from_mut(path: &mut str) -> &mut Path {
155 unsafe { &mut *(path as *mut str as *mut Path) }
157 }
158
159 pub fn validate(path: &str) -> Result<&Path, PathError> {
161 match path {
162 "" | "\"\"" => return Err(PathError::Empty),
163 "::" => return Err(PathError::EmptyComponent),
164 _ => (),
165 }
166
167 if path.len() > u16::MAX as usize {
168 return Err(PathError::TooLong { max: u16::MAX as usize });
169 }
170
171 for result in Iter::new(path) {
172 result?;
173 }
174
175 Ok(Path::new(path))
176 }
177
178 pub const fn kernel_path() -> &'static Path {
180 Path::KERNEL
181 }
182
183 pub const fn exec_path() -> &'static Path {
185 Path::EXEC
186 }
187
188 #[inline]
189 pub const fn as_str(&self) -> &str {
190 &self.inner
191 }
192
193 #[inline]
194 pub fn as_mut_str(&mut self) -> &mut str {
195 &mut self.inner
196 }
197
198 pub fn as_ident(&self) -> Option<Ident> {
203 let mut components = self.components().filter_map(|c| c.ok());
204 match components.next()? {
205 component @ PathComponent::Normal(_) => {
206 if components.next().is_none() {
207 component.to_ident()
208 } else {
209 None
210 }
211 },
212 PathComponent::Root => None,
213 }
214 }
215
216 pub fn to_path_buf(&self) -> PathBuf {
218 PathBuf { inner: self.inner.to_string() }
219 }
220
221 pub fn from_ident(ident: &Ident) -> Cow<'_, Path> {
224 let ident = ident.as_str();
225 if Ident::requires_quoting(ident) {
226 let mut buf = PathBuf::with_capacity(ident.len() + 2);
227 buf.push_component(ident);
228 Cow::Owned(buf)
229 } else {
230 Cow::Borrowed(Path::new(ident))
231 }
232 }
233}
234
235impl Path {
237 pub fn is_empty(&self) -> bool {
239 matches!(&self.inner, "" | "::" | "\"\"")
240 }
241
242 pub fn len(&self) -> usize {
244 self.components().count()
245 }
246
247 pub fn char_len(&self) -> usize {
249 self.inner.chars().count()
250 }
251
252 #[inline]
254 pub fn byte_len(&self) -> usize {
255 self.inner.len()
256 }
257
258 pub fn is_absolute(&self) -> bool {
260 matches!(self.components().next(), Some(Ok(PathComponent::Root)))
261 }
262
263 pub fn to_absolute(&self) -> Cow<'_, Path> {
267 if self.is_absolute() {
268 Cow::Borrowed(self)
269 } else {
270 let mut buf = PathBuf::with_capacity(self.byte_len() + 2);
271 buf.push_component("::");
272 buf.extend_with_components(self.components()).expect("invalid path");
273 Cow::Owned(buf)
274 }
275 }
276
277 pub fn to_relative(&self) -> &Path {
279 match self.inner.strip_prefix("::") {
280 Some(rest) => Path::new(rest),
281 None => self,
282 }
283 }
284
285 pub fn parent(&self) -> Option<&Path> {
291 let mut components = self.components();
292 match components.next_back()?.ok()? {
293 PathComponent::Root => None,
294 _ => Some(components.as_path()),
295 }
296 }
297
298 pub fn components(&self) -> Iter<'_> {
300 Iter::new(&self.inner)
301 }
302
303 pub fn first(&self) -> Option<&str> {
307 self.split_first().map(|(first, _)| first)
308 }
309
310 pub fn last(&self) -> Option<&str> {
314 self.split_last().map(|(last, _)| last)
315 }
316
317 pub fn split_first(&self) -> Option<(&str, &Path)> {
322 let mut components = self.components();
323 match components.next()?.ok()? {
324 PathComponent::Root => {
325 let first = components.next().and_then(|c| c.ok()).map(|c| c.as_str())?;
326 Some((first, components.as_path()))
327 },
328 first @ PathComponent::Normal(_) => Some((first.as_str(), components.as_path())),
329 }
330 }
331
332 pub fn split_last(&self) -> Option<(&str, &Path)> {
337 let mut components = self.components();
338 match components.next_back()?.ok()? {
339 PathComponent::Root => None,
340 last @ PathComponent::Normal(_) => Some((last.as_str(), components.as_path())),
341 }
342 }
343
344 pub fn is_kernel_path(&self) -> bool {
346 match self.inner.strip_prefix("::") {
347 Some(Self::KERNEL_PATH) => true,
348 Some(_) => false,
349 None => &self.inner == Self::KERNEL_PATH,
350 }
351 }
352
353 pub fn is_in_kernel(&self) -> bool {
355 if self.is_kernel_path() {
356 return true;
357 }
358
359 match self.split_last() {
360 Some((_, prefix)) => prefix.is_kernel_path(),
361 None => false,
362 }
363 }
364
365 pub fn is_exec_path(&self) -> bool {
367 match self.inner.strip_prefix("::") {
368 Some(Self::EXEC_PATH) => true,
369 Some(_) => false,
370 None => &self.inner == Self::EXEC_PATH,
371 }
372 }
373
374 pub fn is_in_exec(&self) -> bool {
376 if self.is_exec_path() {
377 return true;
378 }
379
380 match self.split_last() {
381 Some((_, prefix)) => prefix.is_exec_path(),
382 None => false,
383 }
384 }
385
386 #[inline]
394 pub fn starts_with<Prefix>(&self, prefix: &Prefix) -> bool
395 where
396 Prefix: ?Sized,
397 Self: StartsWith<Prefix>,
398 {
399 <Self as StartsWith<Prefix>>::starts_with(self, prefix)
400 }
401
402 #[inline]
410 pub fn starts_with_exactly<Prefix>(&self, prefix: &Prefix) -> bool
411 where
412 Prefix: ?Sized,
413 Self: StartsWith<Prefix>,
414 {
415 <Self as StartsWith<Prefix>>::starts_with_exactly(self, prefix)
416 }
417
418 pub fn strip_prefix<'a>(&'a self, prefix: &Self) -> Option<&'a Self> {
424 let mut components = self.components();
425 for prefix_component in prefix.components() {
426 let prefix_component = prefix_component.expect("invalid prefix path");
435 match (components.next(), prefix_component) {
436 (Some(Ok(PathComponent::Root)), PathComponent::Root) => (),
437 (Some(Ok(c @ PathComponent::Normal(_))), pc @ PathComponent::Normal(_)) => {
438 if c.as_str() != pc.as_str() {
439 return None;
440 }
441 },
442 (Some(Ok(_) | Err(_)) | None, _) => return None,
443 }
444 }
445 Some(components.as_path())
446 }
447
448 #[inline]
461 pub fn join<P>(&self, other: &P) -> PathBuf
462 where
463 P: ?Sized,
464 Path: Join<P>,
465 {
466 <Path as Join<P>>::join(self, other)
467 }
468
469 pub fn canonicalize(&self) -> Result<PathBuf, PathError> {
478 let mut buf = PathBuf::with_capacity(self.byte_len());
479 buf.extend_with_components(self.components())?;
480 Ok(buf)
481 }
482}
483
484impl StartsWith<str> for Path {
485 fn starts_with(&self, prefix: &str) -> bool {
486 let this = self.to_relative();
487 <Path as StartsWith<str>>::starts_with_exactly(this, prefix)
488 }
489
490 #[inline]
491 fn starts_with_exactly(&self, prefix: &str) -> bool {
492 match prefix {
493 "" => true,
494 "::" => self.is_absolute(),
495 prefix => {
496 let mut components = self.components();
497 let prefix = if let Some(prefix) = prefix.strip_prefix("::") {
498 let is_absolute =
499 components.next().is_some_and(|c| matches!(c, Ok(PathComponent::Root)));
500 if !is_absolute {
501 return false;
502 }
503 prefix
504 } else {
505 prefix
506 };
507 components.next().is_some_and(
508 |c| matches!(c, Ok(c @ PathComponent::Normal(_)) if c.as_str() == prefix),
509 )
510 },
511 }
512 }
513}
514
515impl StartsWith<Path> for Path {
516 fn starts_with(&self, prefix: &Path) -> bool {
517 let this = self.to_relative();
518 let prefix = prefix.to_relative();
519 <Path as StartsWith<Path>>::starts_with_exactly(this, prefix)
520 }
521
522 #[inline]
523 fn starts_with_exactly(&self, prefix: &Path) -> bool {
524 let mut components = self.components();
525 for prefix_component in prefix.components() {
526 let prefix_component = prefix_component.expect("invalid prefix path");
535 match (components.next(), prefix_component) {
536 (Some(Ok(PathComponent::Root)), PathComponent::Root) => {},
537 (Some(Ok(c @ PathComponent::Normal(_))), pc @ PathComponent::Normal(_)) => {
538 if c.as_str() != pc.as_str() {
539 return false;
540 }
541 },
542 (Some(Ok(_) | Err(_)) | None, _) => return false,
543 }
544 }
545 true
546 }
547}
548
549impl PartialEq<str> for Path {
550 fn eq(&self, other: &str) -> bool {
551 &self.inner == other
552 }
553}
554
555impl PartialEq<PathBuf> for Path {
556 fn eq(&self, other: &PathBuf) -> bool {
557 &self.inner == other.inner.as_str()
558 }
559}
560
561impl PartialEq<&PathBuf> for Path {
562 fn eq(&self, other: &&PathBuf) -> bool {
563 &self.inner == other.inner.as_str()
564 }
565}
566
567impl PartialEq<Path> for PathBuf {
568 fn eq(&self, other: &Path) -> bool {
569 self.inner.as_str() == &other.inner
570 }
571}
572
573impl PartialEq<&Path> for Path {
574 fn eq(&self, other: &&Path) -> bool {
575 self.inner == other.inner
576 }
577}
578
579impl PartialEq<alloc::boxed::Box<Path>> for Path {
580 fn eq(&self, other: &alloc::boxed::Box<Path>) -> bool {
581 self.inner == other.inner
582 }
583}
584
585impl PartialEq<alloc::rc::Rc<Path>> for Path {
586 fn eq(&self, other: &alloc::rc::Rc<Path>) -> bool {
587 self.inner == other.inner
588 }
589}
590
591impl PartialEq<alloc::sync::Arc<Path>> for Path {
592 fn eq(&self, other: &alloc::sync::Arc<Path>) -> bool {
593 self.inner == other.inner
594 }
595}
596
597impl PartialEq<alloc::borrow::Cow<'_, Path>> for Path {
598 fn eq(&self, other: &alloc::borrow::Cow<'_, Path>) -> bool {
599 self.inner == other.as_ref().inner
600 }
601}
602
603impl fmt::Display for Path {
604 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
605 f.write_str(&self.inner)
606 }
607}
608
609#[cfg(test)]
610mod tests {
611 use super::*;
612
613 #[test]
614 fn test_canonicalize_path_identity() -> Result<(), PathError> {
615 let path = Path::new("foo::bar");
616 let canonicalized = path.canonicalize()?;
617
618 assert_eq!(canonicalized.as_path(), path);
619 Ok(())
620 }
621
622 #[test]
623 fn test_canonicalize_path_kernel_is_absolute() -> Result<(), PathError> {
624 let path = Path::new("$kernel::bar");
625 let canonicalized = path.canonicalize()?;
626
627 let expected = Path::new("::$kernel::bar");
628 assert_eq!(canonicalized.as_path(), expected);
629 Ok(())
630 }
631
632 #[test]
633 fn test_canonicalize_path_exec_is_absolute() -> Result<(), PathError> {
634 let path = Path::new("$exec::$main");
635 let canonicalized = path.canonicalize()?;
636
637 let expected = Path::new("::$exec::$main");
638 assert_eq!(canonicalized.as_path(), expected);
639 Ok(())
640 }
641
642 #[test]
643 fn test_canonicalize_path_remove_unnecessary_quoting() -> Result<(), PathError> {
644 let path = Path::new("foo::\"bar\"");
645 let canonicalized = path.canonicalize()?;
646
647 let expected = Path::new("foo::bar");
648 assert_eq!(canonicalized.as_path(), expected);
649 Ok(())
650 }
651
652 #[test]
653 fn test_canonicalize_path_preserve_necessary_quoting() -> Result<(), PathError> {
654 let path = Path::new("foo::\"bar::baz\"");
655 let canonicalized = path.canonicalize()?;
656
657 assert_eq!(canonicalized.as_path(), path);
658 Ok(())
659 }
660
661 #[test]
662 fn test_canonicalize_path_add_required_quoting_to_components_without_delimiter()
663 -> Result<(), PathError> {
664 let path = Path::new("foo::$bar");
665 let canonicalized = path.canonicalize()?;
666
667 let expected = Path::new("foo::\"$bar\"");
668 assert_eq!(canonicalized.as_path(), expected);
669 Ok(())
670 }
671}