miden_assembly_syntax/ast/path/
path.rs

1use alloc::{
2    borrow::{Borrow, Cow, ToOwned},
3    string::{String, ToString},
4};
5use core::fmt;
6
7use super::{Iter, PathBuf, PathComponent, PathError, StartsWith};
8use crate::ast::Ident;
9
10/// A borrowed reference to a subset of a path, e.g. another [Path] or a [PathBuf]
11#[derive(PartialEq, Eq, PartialOrd, Ord, Hash)]
12#[repr(transparent)]
13pub struct Path {
14    /// A view into the selected components of the path, i.e. the parts delimited by `::`
15    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        let inner = <&'de str as serde::Deserialize<'de>>::deserialize(deserializer)?;
41
42        Ok(Path::new(inner))
43    }
44}
45
46impl ToOwned for Path {
47    type Owned = PathBuf;
48    #[inline]
49    fn to_owned(&self) -> PathBuf {
50        self.to_path_buf()
51    }
52    #[inline]
53    fn clone_into(&self, target: &mut Self::Owned) {
54        self.inner.clone_into(&mut target.inner)
55    }
56}
57
58impl Borrow<Path> for PathBuf {
59    fn borrow(&self) -> &Path {
60        Path::new(self)
61    }
62}
63
64impl AsRef<str> for Path {
65    #[inline]
66    fn as_ref(&self) -> &str {
67        &self.inner
68    }
69}
70
71impl AsRef<Path> for str {
72    #[inline(always)]
73    fn as_ref(&self) -> &Path {
74        unsafe { &*(self as *const str as *const Path) }
75    }
76}
77
78impl AsRef<Path> for Ident {
79    #[inline(always)]
80    fn as_ref(&self) -> &Path {
81        self.as_str().as_ref()
82    }
83}
84
85impl AsRef<Path> for crate::ast::ProcedureName {
86    #[inline(always)]
87    fn as_ref(&self) -> &Path {
88        let ident: &Ident = self.as_ref();
89        ident.as_str().as_ref()
90    }
91}
92
93impl AsRef<Path> for crate::ast::QualifiedProcedureName {
94    #[inline(always)]
95    fn as_ref(&self) -> &Path {
96        self.as_path()
97    }
98}
99
100impl AsRef<Path> for Path {
101    #[inline(always)]
102    fn as_ref(&self) -> &Path {
103        self
104    }
105}
106
107impl From<&Path> for alloc::sync::Arc<Path> {
108    fn from(path: &Path) -> Self {
109        path.to_path_buf().into()
110    }
111}
112
113/// Conversions
114impl Path {
115    /// Path components  must be 255 bytes or less
116    pub const MAX_COMPONENT_LENGTH: usize = u8::MAX as usize;
117
118    /// An empty path for use as a default value, placeholder, comparisons, etc.
119    pub const EMPTY: &Path = unsafe { &*("" as *const str as *const Path) };
120
121    /// Base kernel path.
122    pub const KERNEL_PATH: &str = "$kernel";
123    pub const ABSOLUTE_KERNEL_PATH: &str = "::$kernel";
124    pub const KERNEL: &Path =
125        unsafe { &*(Self::ABSOLUTE_KERNEL_PATH as *const str as *const Path) };
126
127    /// Path for an executable module.
128    pub const EXEC_PATH: &str = "$exec";
129    pub const ABSOLUTE_EXEC_PATH: &str = "::$exec";
130    pub const EXEC: &Path = unsafe { &*(Self::ABSOLUTE_EXEC_PATH as *const str as *const Path) };
131
132    pub fn new<S: AsRef<str> + ?Sized>(path: &S) -> &Path {
133        // SAFETY: The representation of Path is equivalent to str
134        unsafe { &*(path.as_ref() as *const str as *const Path) }
135    }
136
137    pub fn from_mut(path: &mut str) -> &mut Path {
138        // SAFETY: The representation of Path is equivalent to str
139        unsafe { &mut *(path as *mut str as *mut Path) }
140    }
141
142    /// Verify that `path` meets all the requirements for a valid [Path]
143    pub fn validate(path: &str) -> Result<&Path, PathError> {
144        match path {
145            "" => return Err(PathError::Empty),
146            "::" => return Err(PathError::EmptyComponent),
147            _ => (),
148        }
149
150        for result in Iter::new(path) {
151            result?;
152        }
153
154        Ok(Path::new(path))
155    }
156
157    /// Get a [Path] corresponding to [Self::KERNEL_PATH]
158    pub const fn kernel_path() -> &'static Path {
159        Path::KERNEL
160    }
161
162    /// Get a [Path] corresponding to [Self::EXEC_PATH]
163    pub const fn exec_path() -> &'static Path {
164        Path::EXEC
165    }
166
167    #[inline]
168    pub const fn as_str(&self) -> &str {
169        &self.inner
170    }
171
172    #[inline]
173    pub fn as_mut_str(&mut self) -> &mut str {
174        &mut self.inner
175    }
176
177    /// Get an [Ident] that is equivalent to this [Path], so long as the path has only a single
178    /// component.
179    ///
180    /// Returns `None` if the path cannot be losslessly represented as a single component.
181    pub fn as_ident(&self) -> Option<Ident> {
182        let mut components = self.components().filter_map(|c| c.ok());
183        match components.next()? {
184            component @ PathComponent::Normal(_) => {
185                if components.next().is_none() {
186                    component.to_ident()
187                } else {
188                    None
189                }
190            },
191            PathComponent::Root => None,
192        }
193    }
194
195    /// Convert this [Path] to an owned [PathBuf]
196    pub fn to_path_buf(&self) -> PathBuf {
197        PathBuf { inner: self.inner.to_string() }
198    }
199}
200
201/// Accesssors
202impl Path {
203    /// Returns true if this path is empty (i.e. has no components)
204    pub fn is_empty(&self) -> bool {
205        self.inner.is_empty() || &self.inner == "::"
206    }
207
208    /// Returns the number of components in the path
209    pub fn len(&self) -> usize {
210        self.components().count()
211    }
212
213    /// Return the size of the path in [char]s when displayed as a string
214    pub fn char_len(&self) -> usize {
215        self.inner.chars().count()
216    }
217
218    /// Return the size of the path in bytes when displayed as a string
219    #[inline]
220    pub fn byte_len(&self) -> usize {
221        self.inner.len()
222    }
223
224    /// Returns true if this path is an absolute path
225    pub fn is_absolute(&self) -> bool {
226        matches!(
227            self.components().next(),
228            Some(Ok(PathComponent::Root))
229                | Some(Ok(PathComponent::Normal(Self::KERNEL_PATH | Self::EXEC_PATH))),
230        )
231    }
232
233    /// Make this path absolute, if not already
234    ///
235    /// NOTE: This does not _resolve_ the path, it simply ensures the path has the root prefix
236    pub fn to_absolute(&self) -> Cow<'_, Path> {
237        if self.is_absolute() {
238            Cow::Borrowed(self)
239        } else {
240            let mut inner = String::with_capacity(self.byte_len() + 2);
241            inner.push_str("::");
242            inner.push_str(&self.inner);
243            Cow::Owned(PathBuf { inner })
244        }
245    }
246
247    /// Strip the root prefix from this path, if it has one.
248    pub fn to_relative(&self) -> &Path {
249        match self.inner.strip_prefix("::") {
250            Some(rest) => Path::new(rest),
251            None => self,
252        }
253    }
254
255    /// Returns the [Path] without its final component, if there is one.
256    ///
257    /// This means it may return an empty [Path] for relative paths with a single component.
258    ///
259    /// Returns `None` if the path terminates with the root prefix, or if it is empty.
260    pub fn parent(&self) -> Option<&Path> {
261        let mut components = self.components();
262        match components.next_back()?.ok()? {
263            PathComponent::Root => None,
264            _ => Some(components.as_path()),
265        }
266    }
267
268    /// Returns an iterator over all components of the path.
269    pub fn components(&self) -> Iter<'_> {
270        Iter::new(&self.inner)
271    }
272
273    /// Get the first non-root component of this path as a `str`
274    ///
275    /// Returns `None` if the path is empty, or consists only of the root prefix.
276    pub fn first(&self) -> Option<&str> {
277        self.split_first().map(|(first, _)| first)
278    }
279
280    /// Get the first non-root component of this path as a `str`
281    ///
282    /// Returns `None` if the path is empty, or consists only of the root prefix.
283    pub fn last(&self) -> Option<&str> {
284        self.split_last().map(|(last, _)| last)
285    }
286
287    /// Splits this path on the first non-root component, returning it and a new [Path] of the
288    /// remaining components.
289    ///
290    /// Returns `None` if there are no components to split
291    pub fn split_first(&self) -> Option<(&str, &Path)> {
292        let mut components = self.components();
293        match components.next()?.ok()? {
294            PathComponent::Root => {
295                let first = components.next().and_then(|c| c.ok()).map(|c| c.as_str())?;
296                Some((first, components.as_path()))
297            },
298            PathComponent::Normal(first) => Some((first, components.as_path())),
299        }
300    }
301
302    /// Splits this path on the last component, returning it and a new [Path] of the remaining
303    /// components.
304    ///
305    /// Returns `None` if there are no components to split
306    pub fn split_last(&self) -> Option<(&str, &Path)> {
307        let mut components = self.components();
308        match components.next_back()?.ok()? {
309            PathComponent::Root => None,
310            PathComponent::Normal(last) => Some((last, components.as_path())),
311        }
312    }
313
314    /// Returns true if this path is for the root kernel module.
315    pub fn is_kernel_path(&self) -> bool {
316        match self.inner.strip_prefix("::") {
317            Some(Self::KERNEL_PATH) => true,
318            Some(_) => false,
319            None => &self.inner == Self::KERNEL_PATH,
320        }
321    }
322
323    /// Returns true if this path is for the root kernel module or an item in it
324    pub fn is_in_kernel(&self) -> bool {
325        if self.is_kernel_path() {
326            return true;
327        }
328
329        match self.split_last() {
330            Some((_, prefix)) => Self::KERNEL == prefix,
331            None => false,
332        }
333    }
334
335    /// Returns true if this path is for an executable module.
336    pub fn is_exec_path(&self) -> bool {
337        match self.inner.strip_prefix("::") {
338            Some(Self::EXEC_PATH) => true,
339            Some(_) => false,
340            None => &self.inner == Self::EXEC_PATH,
341        }
342    }
343
344    /// Returns true if the current path, sans root component, starts with `prefix`
345    #[inline]
346    pub fn starts_with<Prefix>(&self, prefix: &Prefix) -> bool
347    where
348        Prefix: ?Sized,
349        Self: StartsWith<Prefix>,
350    {
351        <Self as StartsWith<Prefix>>::starts_with(self, prefix)
352    }
353
354    /// Returns true if the current path, including root component, starts with `prefix`
355    #[inline]
356    pub fn starts_with_exactly<Prefix>(&self, prefix: &Prefix) -> bool
357    where
358        Prefix: ?Sized,
359        Self: StartsWith<Prefix>,
360    {
361        <Self as StartsWith<Prefix>>::starts_with_exactly(self, prefix)
362    }
363
364    /// Create an owned [PathBuf] with `path` adjoined to `self`.
365    ///
366    /// If `path` is absolute, it replaces the current path.
367    ///
368    /// See [PathBuf::push] for more details on what it means to adjoin a path.
369    pub fn join(&self, path: impl AsRef<Path>) -> PathBuf {
370        let path = path.as_ref();
371
372        if path.is_empty() {
373            return self.to_path_buf();
374        }
375
376        if self.is_empty() || path.is_absolute() {
377            return path.to_path_buf();
378        }
379
380        let mut buf = self.to_path_buf();
381        buf.push(path);
382
383        buf
384    }
385}
386
387impl StartsWith<str> for Path {
388    fn starts_with(&self, prefix: &str) -> bool {
389        if prefix.is_empty() {
390            return true;
391        }
392        if prefix.starts_with("::") {
393            self.inner.starts_with(prefix)
394        } else {
395            match self.inner.strip_prefix("::") {
396                Some(rest) => rest.starts_with(prefix),
397                None => self.inner.starts_with(prefix),
398            }
399        }
400    }
401
402    #[inline]
403    fn starts_with_exactly(&self, prefix: &str) -> bool {
404        self.inner.starts_with(prefix)
405    }
406}
407
408impl StartsWith<Path> for Path {
409    fn starts_with(&self, prefix: &Path) -> bool {
410        <Self as StartsWith<str>>::starts_with(self, prefix.as_str())
411    }
412
413    #[inline]
414    fn starts_with_exactly(&self, prefix: &Path) -> bool {
415        <Self as StartsWith<str>>::starts_with_exactly(self, prefix.as_str())
416    }
417}
418
419impl PartialEq<str> for Path {
420    fn eq(&self, other: &str) -> bool {
421        &self.inner == other
422    }
423}
424
425impl PartialEq<PathBuf> for Path {
426    fn eq(&self, other: &PathBuf) -> bool {
427        &self.inner == other.inner.as_str()
428    }
429}
430
431impl PartialEq<&PathBuf> for Path {
432    fn eq(&self, other: &&PathBuf) -> bool {
433        &self.inner == other.inner.as_str()
434    }
435}
436
437impl PartialEq<Path> for PathBuf {
438    fn eq(&self, other: &Path) -> bool {
439        self.inner.as_str() == &other.inner
440    }
441}
442
443impl PartialEq<&Path> for Path {
444    fn eq(&self, other: &&Path) -> bool {
445        self.inner == other.inner
446    }
447}
448
449impl PartialEq<alloc::boxed::Box<Path>> for Path {
450    fn eq(&self, other: &alloc::boxed::Box<Path>) -> bool {
451        self.inner == other.inner
452    }
453}
454
455impl PartialEq<alloc::rc::Rc<Path>> for Path {
456    fn eq(&self, other: &alloc::rc::Rc<Path>) -> bool {
457        self.inner == other.inner
458    }
459}
460
461impl PartialEq<alloc::sync::Arc<Path>> for Path {
462    fn eq(&self, other: &alloc::sync::Arc<Path>) -> bool {
463        self.inner == other.inner
464    }
465}
466
467impl PartialEq<alloc::borrow::Cow<'_, Path>> for Path {
468    fn eq(&self, other: &alloc::borrow::Cow<'_, Path>) -> bool {
469        self.inner == other.as_ref().inner
470    }
471}
472
473impl fmt::Display for Path {
474    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
475        f.write_str(&self.inner)
476    }
477}