Skip to main content

miden_assembly_syntax/ast/path/
mod.rs

1mod components;
2mod join;
3#[expect(clippy::module_inception)]
4mod path;
5mod path_buf;
6
7pub use self::{
8    components::{Iter, PathComponent},
9    join::Join,
10    path::Path,
11    path_buf::PathBuf,
12};
13#[cfg(feature = "serde")]
14use crate::debuginfo::Span;
15use crate::diagnostics::{Diagnostic, miette};
16
17/// Represents errors that can occur when creating, parsing, or manipulating [Path]s
18#[derive(Debug, thiserror::Error)]
19pub enum PathError {
20    #[error("invalid item path: cannot be empty")]
21    Empty,
22    #[error("invalid item path component: cannot be empty")]
23    EmptyComponent,
24    #[error("invalid item path component: {0}")]
25    InvalidComponent(crate::ast::IdentError),
26    #[error("invalid item path: contains invalid utf8 byte sequences")]
27    InvalidUtf8,
28    #[error("invalid item path: too long (max {max} bytes)")]
29    TooLong { max: usize },
30    #[error(transparent)]
31    InvalidNamespace(NamespaceError),
32    #[error("cannot join a path with reserved name to other paths")]
33    UnsupportedJoin,
34    #[error("'::' delimiter found where path component was expected")]
35    UnexpectedDelimiter,
36    #[error("path is missing a '::' delimiter between quoted/unquoted components")]
37    MissingPathSeparator,
38    #[error("quoted path component is missing a closing '\"'")]
39    UnclosedQuotedComponent,
40}
41
42/// Represents an error when parsing or validating a library namespace
43#[derive(Debug, thiserror::Error, Diagnostic)]
44pub enum NamespaceError {
45    #[error("invalid library namespace name: cannot be empty")]
46    #[diagnostic()]
47    Empty,
48    #[error("invalid library namespace name: too many characters")]
49    #[diagnostic()]
50    Length,
51    #[error(
52        "invalid character in library namespace: expected lowercase ascii-alphanumeric character or '_'"
53    )]
54    #[diagnostic()]
55    InvalidChars,
56    #[error("invalid library namespace name: must start with lowercase ascii-alphabetic character")]
57    #[diagnostic()]
58    InvalidStart,
59}
60
61/// This trait abstracts over the concept of matching a prefix pattern against a path
62///
63/// To understand the semantics of how a given prefix pattern matches against a path, you must check
64/// the implementation for the type, but all of the provided implementations are broken down into
65/// two categories:
66///
67/// 1. The prefix is a `Path` or `PathBuf`, in which case each component of the prefix is matched
68///    against each component of `self` until the entire prefix has been visited, or a mismatch is
69///    identified.
70/// 2. The prefix is a `str` or something that derefs to `str`, in which case the prefix is matched
71///    against the first component of `self` (which component of `self` is considered "first" is
72///    dependent on whether the match must be exact or not).
73pub trait StartsWith<Prefix: ?Sized> {
74    /// Returns true if the current path, sans root component, starts with `prefix`
75    fn starts_with(&self, prefix: &Prefix) -> bool;
76
77    /// Returns true if the current path, including root component, starts with `prefix`
78    fn starts_with_exactly(&self, prefix: &Prefix) -> bool;
79}
80
81/// Serialize a [Path]-like value
82#[cfg(feature = "serde")]
83pub fn serialize<P, S>(path: P, serializer: S) -> Result<S::Ok, S::Error>
84where
85    P: AsRef<Path>,
86    S: serde::Serializer,
87{
88    use serde::Serialize;
89    path.as_ref().serialize(serializer)
90}
91
92/// Deserialize a [Path]-like value
93#[cfg(feature = "serde")]
94pub fn deserialize<'de, P, D>(deserializer: D) -> Result<P, D::Error>
95where
96    P: From<PathBuf>,
97    D: serde::Deserializer<'de>,
98{
99    let path = <PathBuf as serde::Deserialize>::deserialize(deserializer)?;
100    Ok(P::from(path))
101}
102
103/// Deserialize a [Path]-like value wrapped in a [Span]
104#[cfg(feature = "serde")]
105pub fn deserialize_spanned<'de, P, D>(deserializer: D) -> Result<Span<P>, D::Error>
106where
107    P: From<PathBuf>,
108    D: serde::Deserializer<'de>,
109{
110    let path = <PathBuf as serde::Deserialize>::deserialize(deserializer)?;
111    Ok(Span::unknown(P::from(path)))
112}
113
114#[cfg(feature = "arbitrary")]
115pub mod arbitrary {
116    use alloc::{sync::Arc, vec::Vec};
117
118    use proptest::{arbitrary::Arbitrary, collection::vec, prelude::*};
119
120    use super::*;
121    use crate::ast::{Ident, ident};
122
123    impl Arbitrary for PathBuf {
124        type Parameters = ();
125
126        fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy {
127            pathbuf_random_length(1).boxed()
128        }
129
130        type Strategy = BoxedStrategy<Self>;
131    }
132
133    prop_compose! {
134        /// A strategy to produce a raw vector of between `min` and `max` path components,
135        /// either quoted or bare.
136        fn components_any(min: u8, max: u8)
137                            (components in vec(prop_oneof![
138                                ident::arbitrary::ident_any_random_length(),
139                                ident::arbitrary::bare_ident_any_random_length()
140                             ], (min as usize)..=(max as usize))) -> Vec<Ident> {
141            components
142        }
143    }
144
145    prop_compose! {
146        /// A strategy to produce a raw vector of between `min` and `max` bare path
147        /// components.
148        fn bare_components_any(min: u8, max: u8)
149                            (components in vec(ident::arbitrary::bare_ident_any_random_length(), (min as usize)..=(max as usize))) -> Vec<Ident> {
150            components
151        }
152    }
153
154    prop_compose! {
155        /// A strategy to produce a PathBuf of between `min` and `max` components (either bare or
156        /// quoted).
157        pub fn pathbuf(min: u8, max: u8)
158                      (components in components_any(min, max)) -> PathBuf {
159            let mut buf = PathBuf::default();
160            for component in components {
161                buf.push_component(&component);
162            }
163            buf
164        }
165    }
166
167    prop_compose! {
168        /// A strategy to produce a PathBuf of up between `min` and `max` bare components.
169        pub fn bare_pathbuf(min: u8, max: u8)
170                      (components in bare_components_any(min, max)) -> PathBuf {
171            let mut buf = PathBuf::default();
172            for component in components {
173                buf.push_component(&component);
174            }
175            buf
176        }
177    }
178
179    prop_compose! {
180        /// A strategy to produce a PathBuf of between `min` and `max` prefix components (either
181        /// bare or quoted), with a constant identifier as the last component.
182        ///
183        /// The returned PathBuf will always have at least one component
184        pub fn constant_pathbuf(min: u8, max: u8)
185                        (prefix in components_any(min, max), name in ident::arbitrary::const_ident_any_random_length()) -> PathBuf {
186            let mut buf = PathBuf::default();
187            for component in prefix {
188                buf.push_component(&component);
189            }
190            buf.push_component(&name);
191            buf
192        }
193    }
194
195    prop_compose! {
196        /// A strategy to produce a PathBuf corresponding to a built-in type reference.
197        ///
198        /// The returned PathBuf will always have a single component
199        pub fn builtin_type_pathbuf()
200                (name in ident::arbitrary::builtin_type_any()) -> PathBuf {
201            PathBuf::from(name)
202        }
203    }
204
205    prop_compose! {
206        /// A strategy to produce a PathBuf of up to between `min` and `max` prefix components
207        /// (either bare or quoted), with a user-defined type identifier as the last component.
208        pub fn user_defined_type_pathbuf(min: u8, max: u8)
209                          ((name, prefix) in (ident::arbitrary::bare_ident_any_random_length(), components_any(min, max))) -> PathBuf {
210            let mut buf = PathBuf::default();
211            for component in prefix {
212                buf.push_component(&component);
213            }
214            buf.push_component(&name);
215            buf
216        }
217    }
218
219    prop_compose! {
220        /// A strategy to produce a PathBuf corresponding to a valid `TypeExpr::Ref`, where
221        /// user-defined type paths will have between `min` and `max` components.
222        pub fn type_pathbuf(min: u8, max: u8)
223                (path in prop_oneof![
224                    1 => user_defined_type_pathbuf(min, max),
225                    2 => builtin_type_pathbuf()
226                ]) -> PathBuf {
227            path
228        }
229    }
230
231    prop_compose! {
232        /// Generate a PathBuf of random length, but at least `min` components.
233        ///
234        /// The returned PathBuf will always have at least one component, regardless of `min`
235        pub fn pathbuf_random_length(min: u8)
236                (max in min..=core::cmp::max(min.saturating_add(1), 10))
237                (path in pathbuf(min, max)) -> PathBuf {
238            path
239        }
240    }
241
242    prop_compose! {
243        /// Generate a PathBuf of random length, but at least `min` components.
244        ///
245        /// The returned PathBuf will always have at least one component, regardless of `min`.
246        ///
247        /// All components of the path will be valid bare identifiers.
248        pub fn bare_pathbuf_random_length(min: u8)
249                (max in min..=core::cmp::max(min.saturating_add(1), 10))
250                (path in bare_pathbuf(min, max)) -> PathBuf {
251            path
252        }
253    }
254
255    prop_compose! {
256        /// Generate a PathBuf of random length, but at least `min` prefix components, that is valid
257        /// for use with constant items.
258        ///
259        /// The returned PathBuf will always have at least one component, the name of the constant.
260        pub fn constant_pathbuf_random_length(min: u8)
261                (max in min..=core::cmp::max(min.saturating_add(1), 10))
262                (path in constant_pathbuf(min, max)) -> PathBuf {
263            path
264        }
265    }
266
267    prop_compose! {
268        /// Generate a PathBuf of random length, but at least `min` prefix components, that is valid
269        /// for use as a type reference.
270        ///
271        /// The returned PathBuf will always have at least one component, the name of the type.
272        pub fn type_pathbuf_random_length(min: u8)
273                (max in min..=core::cmp::max(min.saturating_add(1), 10))
274                (path in type_pathbuf(min, max)) -> PathBuf {
275            path
276        }
277    }
278
279    prop_compose! {
280        /// Generate a PathBuf of random length, but at least `min` prefix components, that is valid
281        /// for use with user-defined type items.
282        ///
283        /// The returned PathBuf will always have at least one component, the name of the type.
284        pub fn user_defined_type_pathbuf_random_length(min: u8)
285                (max in min..=core::cmp::max(min.saturating_add(1), 10))
286                (path in user_defined_type_pathbuf(min, max)) -> PathBuf {
287            path
288        }
289    }
290
291    prop_compose! {
292        /// Generate a `Arc<Path>` of random length, but at least `min` components.
293        pub fn path_random_length(min: u8)
294            (path in pathbuf_random_length(min)) -> Arc<Path> {
295            path.into()
296        }
297    }
298
299    prop_compose! {
300        /// Generate a `Arc<Path>` of random length, but at least `min` components.
301        ///
302        /// All components of the path will be valid bare identifiers.
303        pub fn bare_path_random_length(min: u8)
304            (path in bare_pathbuf_random_length(min)) -> Arc<Path> {
305            path.into()
306        }
307    }
308
309    prop_compose! {
310        /// Generate a `Arc<Path>` of random length, but at least `min` prefix components, that is
311        /// valid for use with constant items.
312        ///
313        /// The returned PathBuf will always have at least one component, the name of the constant.
314        pub fn constant_path_random_length(min: u8)
315            (path in constant_pathbuf_random_length(min)) -> Arc<Path> {
316            path.into()
317        }
318    }
319
320    prop_compose! {
321        /// Generate a `Arc<Path>` of random length, but at least `min` prefix components, that is
322        /// valid for use with type references.
323        ///
324        /// The returned PathBuf will always have at least one component, the name of the type.
325        pub fn type_path_random_length(min: u8)
326            (path in type_pathbuf_random_length(min)) -> Arc<Path> {
327            path.into()
328        }
329    }
330
331    prop_compose! {
332        /// Generate a `Arc<Path>` of random length, but at least `min` prefix components, that is
333        /// valid for use with user-defined type items.
334        ///
335        /// The returned PathBuf will always have at least one component, the name of the type.
336        pub fn user_defined_type_path_random_length(min: u8)
337            (path in user_defined_type_pathbuf_random_length(min)) -> Arc<Path> {
338            path.into()
339        }
340    }
341}