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