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
60///
61/// To understand the semantics of how a given prefix pattern matches against a path, you must check
62/// the implementation for the type, but all of the provided implementations are broken down into
63/// two categories:
64///
65/// 1. The prefix is a `Path` or `PathBuf`, in which case each component of the prefix is matched
66///    against each component of `self` until the entire prefix has been visited, or a mismatch is
67///    identified.
68/// 2. The prefix is a `str` or something that derefs to `str`, in which case the prefix is matched
69///    against the first component of `self` (which component of `self` is considered "first" is
70///    dependent on whether the match must be exact or not).
71pub trait StartsWith<Prefix: ?Sized> {
72    /// Returns true if the current path, sans root component, starts with `prefix`
73    fn starts_with(&self, prefix: &Prefix) -> bool;
74
75    /// Returns true if the current path, including root component, starts with `prefix`
76    fn starts_with_exactly(&self, prefix: &Prefix) -> bool;
77}
78
79/// Serialize a [Path]-like value
80#[cfg(feature = "serde")]
81pub fn serialize<P, S>(path: P, serializer: S) -> Result<S::Ok, S::Error>
82where
83    P: AsRef<Path>,
84    S: serde::Serializer,
85{
86    use serde::Serialize;
87    path.as_ref().serialize(serializer)
88}
89
90/// Deserialize a [Path]-like value
91#[cfg(feature = "serde")]
92pub fn deserialize<'de, P, D>(deserializer: D) -> Result<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(P::from(path))
99}
100
101/// Deserialize a [Path]-like value wrapped in a [Span]
102#[cfg(feature = "serde")]
103pub fn deserialize_spanned<'de, P, D>(deserializer: D) -> Result<Span<P>, D::Error>
104where
105    P: From<PathBuf>,
106    D: serde::Deserializer<'de>,
107{
108    let path = <PathBuf as serde::Deserialize>::deserialize(deserializer)?;
109    Ok(Span::unknown(P::from(path)))
110}
111
112#[cfg(feature = "arbitrary")]
113pub mod arbitrary {
114    use alloc::{sync::Arc, vec::Vec};
115
116    use proptest::{arbitrary::Arbitrary, collection::vec, prelude::*};
117
118    use super::*;
119    use crate::ast::{Ident, ident};
120
121    impl Arbitrary for PathBuf {
122        type Parameters = ();
123
124        fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy {
125            pathbuf_random_length(1).boxed()
126        }
127
128        type Strategy = BoxedStrategy<Self>;
129    }
130
131    prop_compose! {
132        /// A strategy to produce a raw vector of between `min` and `max` path components,
133        /// either quoted or bare.
134        fn components_any(min: u8, max: u8)
135                            (components in vec(prop_oneof![
136                                ident::arbitrary::ident_any_random_length(),
137                                ident::arbitrary::bare_ident_any_random_length()
138                             ], (min as usize)..=(max as usize))) -> Vec<Ident> {
139            components
140        }
141    }
142
143    prop_compose! {
144        /// A strategy to produce a raw vector of between `min` and `max` bare path
145        /// components.
146        fn bare_components_any(min: u8, max: u8)
147                            (components in vec(ident::arbitrary::bare_ident_any_random_length(), (min as usize)..=(max as usize))) -> Vec<Ident> {
148            components
149        }
150    }
151
152    prop_compose! {
153        /// A strategy to produce a PathBuf of between `min` and `max` components (either bare or
154        /// quoted).
155        pub fn pathbuf(min: u8, max: u8)
156                      (components in components_any(min, max)) -> PathBuf {
157            let mut buf = PathBuf::default();
158            for component in components {
159                buf.push_component(&component);
160            }
161            buf
162        }
163    }
164
165    prop_compose! {
166        /// A strategy to produce a PathBuf of up between `min` and `max` bare components.
167        pub fn bare_pathbuf(min: u8, max: u8)
168                      (components in bare_components_any(min, max)) -> PathBuf {
169            let mut buf = PathBuf::default();
170            for component in components {
171                buf.push_component(&component);
172            }
173            buf
174        }
175    }
176
177    prop_compose! {
178        /// A strategy to produce a PathBuf of between `min` and `max` prefix components (either
179        /// bare or quoted), with a constant identifier as the last component.
180        ///
181        /// The returned PathBuf will always have at least one component
182        pub fn constant_pathbuf(min: u8, max: u8)
183                        (prefix in components_any(min, max), name in ident::arbitrary::const_ident_any_random_length()) -> PathBuf {
184            let mut buf = PathBuf::default();
185            for component in prefix {
186                buf.push_component(&component);
187            }
188            buf.push_component(&name);
189            buf
190        }
191    }
192
193    prop_compose! {
194        /// A strategy to produce a PathBuf corresponding to a built-in type reference.
195        ///
196        /// The returned PathBuf will always have a single component
197        pub fn builtin_type_pathbuf()
198                (name in ident::arbitrary::builtin_type_any()) -> PathBuf {
199            PathBuf::from(name)
200        }
201    }
202
203    prop_compose! {
204        /// A strategy to produce a PathBuf of up to between `min` and `max` prefix components
205        /// (either bare or quoted), with a user-defined type identifier as the last component.
206        pub fn user_defined_type_pathbuf(min: u8, max: u8)
207                          ((name, prefix) in (ident::arbitrary::bare_ident_any_random_length(), components_any(min, max))) -> PathBuf {
208            let mut buf = PathBuf::default();
209            for component in prefix {
210                buf.push_component(&component);
211            }
212            buf.push_component(&name);
213            buf
214        }
215    }
216
217    prop_compose! {
218        /// A strategy to produce a PathBuf corresponding to a valid `TypeExpr::Ref`, where
219        /// user-defined type paths will have between `min` and `max` components.
220        pub fn type_pathbuf(min: u8, max: u8)
221                (path in prop_oneof![
222                    1 => user_defined_type_pathbuf(min, max),
223                    2 => builtin_type_pathbuf()
224                ]) -> 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        pub fn pathbuf_random_length(min: u8)
234                (max in min..=core::cmp::max(min.saturating_add(1), 10))
235                (path in pathbuf(min, max)) -> PathBuf {
236            path
237        }
238    }
239
240    prop_compose! {
241        /// Generate a PathBuf of random length, but at least `min` components.
242        ///
243        /// The returned PathBuf will always have at least one component, regardless of `min`.
244        ///
245        /// All components of the path will be valid bare identifiers.
246        pub fn bare_pathbuf_random_length(min: u8)
247                (max in min..=core::cmp::max(min.saturating_add(1), 10))
248                (path in bare_pathbuf(min, max)) -> PathBuf {
249            path
250        }
251    }
252
253    prop_compose! {
254        /// Generate a PathBuf of random length, but at least `min` prefix components, that is valid
255        /// for use with constant items.
256        ///
257        /// The returned PathBuf will always have at least one component, the name of the constant.
258        pub fn constant_pathbuf_random_length(min: u8)
259                (max in min..=core::cmp::max(min.saturating_add(1), 10))
260                (path in constant_pathbuf(min, max)) -> PathBuf {
261            path
262        }
263    }
264
265    prop_compose! {
266        /// Generate a PathBuf of random length, but at least `min` prefix components, that is valid
267        /// for use as a type reference.
268        ///
269        /// The returned PathBuf will always have at least one component, the name of the type.
270        pub fn type_pathbuf_random_length(min: u8)
271                (max in min..=core::cmp::max(min.saturating_add(1), 10))
272                (path in type_pathbuf(min, max)) -> PathBuf {
273            path
274        }
275    }
276
277    prop_compose! {
278        /// Generate a PathBuf of random length, but at least `min` prefix components, that is valid
279        /// for use with user-defined type items.
280        ///
281        /// The returned PathBuf will always have at least one component, the name of the type.
282        pub fn user_defined_type_pathbuf_random_length(min: u8)
283                (max in min..=core::cmp::max(min.saturating_add(1), 10))
284                (path in user_defined_type_pathbuf(min, max)) -> PathBuf {
285            path
286        }
287    }
288
289    prop_compose! {
290        /// Generate a `Arc<Path>` of random length, but at least `min` components.
291        pub fn path_random_length(min: u8)
292            (path in pathbuf_random_length(min)) -> Arc<Path> {
293            path.into()
294        }
295    }
296
297    prop_compose! {
298        /// Generate a `Arc<Path>` of random length, but at least `min` components.
299        ///
300        /// All components of the path will be valid bare identifiers.
301        pub fn bare_path_random_length(min: u8)
302            (path in bare_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 constant items.
310        ///
311        /// The returned PathBuf will always have at least one component, the name of the constant.
312        pub fn constant_path_random_length(min: u8)
313            (path in constant_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 type references.
321        ///
322        /// The returned PathBuf will always have at least one component, the name of the type.
323        pub fn type_path_random_length(min: u8)
324            (path in type_pathbuf_random_length(min)) -> Arc<Path> {
325            path.into()
326        }
327    }
328
329    prop_compose! {
330        /// Generate a `Arc<Path>` of random length, but at least `min` prefix components, that is
331        /// valid for use with user-defined type items.
332        ///
333        /// The returned PathBuf will always have at least one component, the name of the type.
334        pub fn user_defined_type_path_random_length(min: u8)
335            (path in user_defined_type_pathbuf_random_length(min)) -> Arc<Path> {
336            path.into()
337        }
338    }
339}