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 for result in Iter::new(path) {
168 result?;
169 }
170
171 Ok(Path::new(path))
172 }
173
174 pub const fn kernel_path() -> &'static Path {
176 Path::KERNEL
177 }
178
179 pub const fn exec_path() -> &'static Path {
181 Path::EXEC
182 }
183
184 #[inline]
185 pub const fn as_str(&self) -> &str {
186 &self.inner
187 }
188
189 #[inline]
190 pub fn as_mut_str(&mut self) -> &mut str {
191 &mut self.inner
192 }
193
194 pub fn as_ident(&self) -> Option<Ident> {
199 let mut components = self.components().filter_map(|c| c.ok());
200 match components.next()? {
201 component @ PathComponent::Normal(_) => {
202 if components.next().is_none() {
203 component.to_ident()
204 } else {
205 None
206 }
207 },
208 PathComponent::Root => None,
209 }
210 }
211
212 pub fn to_path_buf(&self) -> PathBuf {
214 PathBuf { inner: self.inner.to_string() }
215 }
216}
217
218impl Path {
220 pub fn is_empty(&self) -> bool {
222 matches!(&self.inner, "" | "::" | "\"\"")
223 }
224
225 pub fn len(&self) -> usize {
227 self.components().count()
228 }
229
230 pub fn char_len(&self) -> usize {
232 self.inner.chars().count()
233 }
234
235 #[inline]
237 pub fn byte_len(&self) -> usize {
238 self.inner.len()
239 }
240
241 pub fn is_absolute(&self) -> bool {
243 matches!(self.components().next(), Some(Ok(PathComponent::Root)))
244 }
245
246 pub fn to_absolute(&self) -> Cow<'_, Path> {
250 if self.is_absolute() {
251 Cow::Borrowed(self)
252 } else {
253 let mut buf = PathBuf::with_capacity(self.byte_len() + 2);
254 buf.push_component("::");
255 buf.extend_with_components(self.components()).expect("invalid path");
256 Cow::Owned(buf)
257 }
258 }
259
260 pub fn to_relative(&self) -> &Path {
262 match self.inner.strip_prefix("::") {
263 Some(rest) => Path::new(rest),
264 None => self,
265 }
266 }
267
268 pub fn parent(&self) -> Option<&Path> {
274 let mut components = self.components();
275 match components.next_back()?.ok()? {
276 PathComponent::Root => None,
277 _ => Some(components.as_path()),
278 }
279 }
280
281 pub fn components(&self) -> Iter<'_> {
283 Iter::new(&self.inner)
284 }
285
286 pub fn first(&self) -> Option<&str> {
290 self.split_first().map(|(first, _)| first)
291 }
292
293 pub fn last(&self) -> Option<&str> {
297 self.split_last().map(|(last, _)| last)
298 }
299
300 pub fn split_first(&self) -> Option<(&str, &Path)> {
305 let mut components = self.components();
306 match components.next()?.ok()? {
307 PathComponent::Root => {
308 let first = components.next().and_then(|c| c.ok()).map(|c| c.as_str())?;
309 Some((first, components.as_path()))
310 },
311 first @ PathComponent::Normal(_) => Some((first.as_str(), components.as_path())),
312 }
313 }
314
315 pub fn split_last(&self) -> Option<(&str, &Path)> {
320 let mut components = self.components();
321 match components.next_back()?.ok()? {
322 PathComponent::Root => None,
323 last @ PathComponent::Normal(_) => Some((last.as_str(), components.as_path())),
324 }
325 }
326
327 pub fn is_kernel_path(&self) -> bool {
329 match self.inner.strip_prefix("::") {
330 Some(Self::KERNEL_PATH) => true,
331 Some(_) => false,
332 None => &self.inner == Self::KERNEL_PATH,
333 }
334 }
335
336 pub fn is_in_kernel(&self) -> bool {
338 if self.is_kernel_path() {
339 return true;
340 }
341
342 match self.split_last() {
343 Some((_, prefix)) => prefix.is_kernel_path(),
344 None => false,
345 }
346 }
347
348 pub fn is_exec_path(&self) -> bool {
350 match self.inner.strip_prefix("::") {
351 Some(Self::EXEC_PATH) => true,
352 Some(_) => false,
353 None => &self.inner == Self::EXEC_PATH,
354 }
355 }
356
357 pub fn is_in_exec(&self) -> bool {
359 if self.is_exec_path() {
360 return true;
361 }
362
363 match self.split_last() {
364 Some((_, prefix)) => prefix.is_exec_path(),
365 None => false,
366 }
367 }
368
369 #[inline]
371 pub fn starts_with<Prefix>(&self, prefix: &Prefix) -> bool
372 where
373 Prefix: ?Sized,
374 Self: StartsWith<Prefix>,
375 {
376 <Self as StartsWith<Prefix>>::starts_with(self, prefix)
377 }
378
379 #[inline]
381 pub fn starts_with_exactly<Prefix>(&self, prefix: &Prefix) -> bool
382 where
383 Prefix: ?Sized,
384 Self: StartsWith<Prefix>,
385 {
386 <Self as StartsWith<Prefix>>::starts_with_exactly(self, prefix)
387 }
388
389 #[inline]
402 pub fn join<P>(&self, other: &P) -> PathBuf
403 where
404 P: ?Sized,
405 Path: Join<P>,
406 {
407 <Path as Join<P>>::join(self, other)
408 }
409
410 pub fn canonicalize(&self) -> Result<PathBuf, PathError> {
419 let mut buf = PathBuf::with_capacity(self.byte_len());
420 buf.extend_with_components(self.components())?;
421 Ok(buf)
422 }
423}
424
425impl StartsWith<str> for Path {
426 fn starts_with(&self, prefix: &str) -> bool {
427 if prefix.is_empty() {
428 return true;
429 }
430 if prefix.starts_with("::") {
431 self.inner.starts_with(prefix)
432 } else {
433 match self.inner.strip_prefix("::") {
434 Some(rest) => rest.starts_with(prefix),
435 None => self.inner.starts_with(prefix),
436 }
437 }
438 }
439
440 #[inline]
441 fn starts_with_exactly(&self, prefix: &str) -> bool {
442 self.inner.starts_with(prefix)
443 }
444}
445
446impl StartsWith<Path> for Path {
447 fn starts_with(&self, prefix: &Path) -> bool {
448 <Self as StartsWith<str>>::starts_with(self, prefix.as_str())
449 }
450
451 #[inline]
452 fn starts_with_exactly(&self, prefix: &Path) -> bool {
453 <Self as StartsWith<str>>::starts_with_exactly(self, prefix.as_str())
454 }
455}
456
457impl PartialEq<str> for Path {
458 fn eq(&self, other: &str) -> bool {
459 &self.inner == other
460 }
461}
462
463impl PartialEq<PathBuf> for Path {
464 fn eq(&self, other: &PathBuf) -> bool {
465 &self.inner == other.inner.as_str()
466 }
467}
468
469impl PartialEq<&PathBuf> for Path {
470 fn eq(&self, other: &&PathBuf) -> bool {
471 &self.inner == other.inner.as_str()
472 }
473}
474
475impl PartialEq<Path> for PathBuf {
476 fn eq(&self, other: &Path) -> bool {
477 self.inner.as_str() == &other.inner
478 }
479}
480
481impl PartialEq<&Path> for Path {
482 fn eq(&self, other: &&Path) -> bool {
483 self.inner == other.inner
484 }
485}
486
487impl PartialEq<alloc::boxed::Box<Path>> for Path {
488 fn eq(&self, other: &alloc::boxed::Box<Path>) -> bool {
489 self.inner == other.inner
490 }
491}
492
493impl PartialEq<alloc::rc::Rc<Path>> for Path {
494 fn eq(&self, other: &alloc::rc::Rc<Path>) -> bool {
495 self.inner == other.inner
496 }
497}
498
499impl PartialEq<alloc::sync::Arc<Path>> for Path {
500 fn eq(&self, other: &alloc::sync::Arc<Path>) -> bool {
501 self.inner == other.inner
502 }
503}
504
505impl PartialEq<alloc::borrow::Cow<'_, Path>> for Path {
506 fn eq(&self, other: &alloc::borrow::Cow<'_, Path>) -> bool {
507 self.inner == other.as_ref().inner
508 }
509}
510
511impl fmt::Display for Path {
512 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
513 f.write_str(&self.inner)
514 }
515}
516
517#[cfg(test)]
518mod tests {
519 use super::*;
520
521 #[test]
522 fn test_canonicalize_path_identity() -> Result<(), PathError> {
523 let path = Path::new("foo::bar");
524 let canonicalized = path.canonicalize()?;
525
526 assert_eq!(canonicalized.as_path(), path);
527 Ok(())
528 }
529
530 #[test]
531 fn test_canonicalize_path_kernel_is_absolute() -> Result<(), PathError> {
532 let path = Path::new("$kernel::bar");
533 let canonicalized = path.canonicalize()?;
534
535 let expected = Path::new("::$kernel::bar");
536 assert_eq!(canonicalized.as_path(), expected);
537 Ok(())
538 }
539
540 #[test]
541 fn test_canonicalize_path_exec_is_absolute() -> Result<(), PathError> {
542 let path = Path::new("$exec::$main");
543 let canonicalized = path.canonicalize()?;
544
545 let expected = Path::new("::$exec::$main");
546 assert_eq!(canonicalized.as_path(), expected);
547 Ok(())
548 }
549
550 #[test]
551 fn test_canonicalize_path_remove_unnecessary_quoting() -> Result<(), PathError> {
552 let path = Path::new("foo::\"bar\"");
553 let canonicalized = path.canonicalize()?;
554
555 let expected = Path::new("foo::bar");
556 assert_eq!(canonicalized.as_path(), expected);
557 Ok(())
558 }
559
560 #[test]
561 fn test_canonicalize_path_preserve_necessary_quoting() -> Result<(), PathError> {
562 let path = Path::new("foo::\"bar::baz\"");
563 let canonicalized = path.canonicalize()?;
564
565 assert_eq!(canonicalized.as_path(), path);
566 Ok(())
567 }
568
569 #[test]
570 fn test_canonicalize_path_add_required_quoting_to_components_without_delimiter()
571 -> Result<(), PathError> {
572 let path = Path::new("foo::$bar");
573 let canonicalized = path.canonicalize()?;
574
575 let expected = Path::new("foo::\"$bar\"");
576 assert_eq!(canonicalized.as_path(), expected);
577 Ok(())
578 }
579}