miden_assembly_syntax/ast/path/
mod.rs

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