miden_assembly_syntax/library/
namespace.rs1#![allow(unused_assignments)]
3
4use alloc::{string::ToString, sync::Arc};
5use core::{
6 fmt,
7 str::{self, FromStr},
8};
9
10use miden_core::utils::{
11 ByteReader, ByteWriter, Deserializable, DeserializationError, Serializable,
12};
13use miden_debug_types::Span;
14use miden_utils_diagnostics::{Diagnostic, miette};
15
16use crate::{LibraryPath, ast::Ident};
17
18#[derive(Debug, thiserror::Error, Diagnostic)]
23pub enum LibraryNamespaceError {
24 #[error("invalid library namespace name: cannot be empty")]
25 #[diagnostic()]
26 Empty,
27 #[error("invalid library namespace name: too many characters")]
28 #[diagnostic()]
29 Length,
30 #[error(
31 "invalid character in library namespace: expected lowercase ascii-alphanumeric character or '_'"
32 )]
33 #[diagnostic()]
34 InvalidChars,
35 #[error("invalid library namespace name: must start with lowercase ascii-alphabetic character")]
36 #[diagnostic()]
37 InvalidStart,
38}
39
40#[derive(Default, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
42#[repr(u8)]
43#[cfg_attr(
44 all(feature = "arbitrary", test),
45 miden_test_serde_macros::serde_test(winter_serde(true))
46)]
47pub enum LibraryNamespace {
48 Kernel = 0,
50 Exec,
52 #[default]
54 Anon,
55 User(Arc<str>),
57}
58
59impl LibraryNamespace {
62 pub const MAX_LENGTH: usize = u8::MAX as usize;
64
65 pub const KERNEL_PATH: &'static str = "$kernel";
67
68 pub const EXEC_PATH: &'static str = "$exec";
70
71 pub const ANON_PATH: &'static str = "$anon";
73}
74
75impl LibraryNamespace {
78 pub fn new<S>(source: S) -> Result<Self, LibraryNamespaceError>
80 where
81 S: AsRef<str>,
82 {
83 source.as_ref().parse()
84 }
85
86 pub fn from_ident_unchecked(name: Ident) -> Self {
90 match name.as_str() {
91 Self::KERNEL_PATH => Self::Kernel,
92 Self::EXEC_PATH => Self::Exec,
93 Self::ANON_PATH => Self::Anon,
94 _ => Self::User(name.into_inner()),
95 }
96 }
97
98 pub fn strip_path_prefix(path: &str) -> Result<(Self, &str), LibraryNamespaceError> {
101 match path.split_once("::") {
102 Some((ns, rest)) => ns.parse().map(|ns| (ns, rest)),
103 None => path.parse().map(|ns| (ns, "")),
104 }
105 }
106}
107
108impl LibraryNamespace {
111 pub fn is_reserved(&self) -> bool {
113 !matches!(self, Self::User(_))
114 }
115
116 pub fn validate(source: impl AsRef<str>) -> Result<(), LibraryNamespaceError> {
126 let source = source.as_ref();
127 if source.is_empty() {
128 return Err(LibraryNamespaceError::Empty);
129 }
130 if matches!(source, Self::KERNEL_PATH | Self::EXEC_PATH | Self::ANON_PATH) {
131 return Ok(());
132 }
133 if source.len() > Self::MAX_LENGTH {
134 return Err(LibraryNamespaceError::Length);
135 }
136 if !source.starts_with(|c: char| c.is_ascii_lowercase() && c.is_ascii_alphabetic()) {
137 return Err(LibraryNamespaceError::InvalidStart);
138 }
139 if !source.chars().all(|c| c.is_ascii_graphic() || c.is_alphanumeric()) {
140 return Err(LibraryNamespaceError::InvalidChars);
141 }
142 Ok(())
143 }
144}
145
146impl LibraryNamespace {
149 pub fn as_str(&self) -> &str {
151 match self {
152 Self::Kernel => Self::KERNEL_PATH,
153 Self::Exec => Self::EXEC_PATH,
154 Self::Anon => Self::ANON_PATH,
155 Self::User(path) => path,
156 }
157 }
158
159 pub fn as_refcounted_str(&self) -> Arc<str> {
161 match self {
162 Self::User(path) => path.clone(),
163 other => Arc::from(other.as_str().to_string().into_boxed_str()),
164 }
165 }
166
167 pub fn to_path(&self) -> LibraryPath {
169 LibraryPath::from(self.clone())
170 }
171
172 pub fn to_ident(&self) -> Ident {
174 Ident::from_raw_parts(Span::unknown(self.as_refcounted_str()))
175 }
176
177 #[cfg(feature = "serde")]
178 const fn tag(&self) -> u8 {
179 unsafe { *(self as *const Self).cast::<u8>() }
186 }
187}
188
189impl core::ops::Deref for LibraryNamespace {
190 type Target = str;
191
192 fn deref(&self) -> &Self::Target {
193 self.as_str()
194 }
195}
196
197impl AsRef<str> for LibraryNamespace {
198 fn as_ref(&self) -> &str {
199 self.as_str()
200 }
201}
202
203impl fmt::Display for LibraryNamespace {
204 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
205 f.write_str(self.as_str())
206 }
207}
208
209impl FromStr for LibraryNamespace {
210 type Err = LibraryNamespaceError;
211
212 fn from_str(s: &str) -> Result<Self, Self::Err> {
213 match s {
214 Self::KERNEL_PATH => Ok(Self::Kernel),
215 Self::EXEC_PATH => Ok(Self::Exec),
216 Self::ANON_PATH => Ok(Self::Anon),
217 other => {
218 Self::validate(other)?;
219 Ok(Self::User(Arc::from(other.to_string().into_boxed_str())))
220 },
221 }
222 }
223}
224
225impl TryFrom<Ident> for LibraryNamespace {
226 type Error = LibraryNamespaceError;
227 fn try_from(ident: Ident) -> Result<Self, Self::Error> {
228 match ident.as_str() {
229 Self::KERNEL_PATH => Ok(Self::Kernel),
230 Self::EXEC_PATH => Ok(Self::Exec),
231 Self::ANON_PATH => Ok(Self::Anon),
232 other => Self::new(other),
233 }
234 }
235}
236
237impl Serializable for LibraryNamespace {
241 fn write_into<W: ByteWriter>(&self, target: &mut W) {
242 let bytes = self.as_bytes();
244 assert!(bytes.len() <= u8::MAX as usize, "namespace too long");
245
246 target.write_u8(bytes.len() as u8);
247 target.write_bytes(bytes);
248 }
249}
250
251impl Deserializable for LibraryNamespace {
252 fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
253 let num_bytes = source.read_u8()? as usize;
254 let name = source.read_slice(num_bytes)?;
255 let name =
256 str::from_utf8(name).map_err(|e| DeserializationError::InvalidValue(e.to_string()))?;
257 Self::new(name).map_err(|e| DeserializationError::InvalidValue(e.to_string()))
258 }
259}
260
261#[cfg(feature = "serde")]
262impl serde::Serialize for LibraryNamespace {
263 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
264 where
265 S: serde::Serializer,
266 {
267 if serializer.is_human_readable() {
268 match self {
269 Self::Kernel => serializer.serialize_str(Self::KERNEL_PATH),
270 Self::Exec => serializer.serialize_str(Self::EXEC_PATH),
271 Self::Anon => serializer.serialize_str(Self::ANON_PATH),
272 Self::User(ns) => serializer.serialize_str(ns),
273 }
274 } else {
275 use serde::ser::SerializeTupleVariant;
276 let tag = self.tag() as u32;
277 match self {
278 Self::Kernel => {
279 serializer.serialize_unit_variant("LibraryNamespace", tag, "Kernel")
280 },
281 Self::Exec => serializer.serialize_unit_variant("LibraryNamespace", tag, "Exec"),
282 Self::Anon => serializer.serialize_unit_variant("LibraryNamespace", tag, "Anon"),
283 Self::User(custom) => {
284 let mut tuple =
285 serializer.serialize_tuple_variant("LibraryNamespace", tag, "User", 1)?;
286 tuple.serialize_field(&custom.as_ref())?;
287 tuple.end()
288 },
289 }
290 }
291 }
292}
293
294#[cfg(feature = "serde")]
295impl<'de> serde::Deserialize<'de> for LibraryNamespace {
296 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
297 where
298 D: serde::Deserializer<'de>,
299 {
300 use serde::de::{SeqAccess, Unexpected};
301
302 if deserializer.is_human_readable() {
303 let name = <&'de str as serde::Deserialize>::deserialize(deserializer)?;
304 match name {
305 Self::KERNEL_PATH => Ok(Self::Kernel),
306 Self::EXEC_PATH => Ok(Self::Exec),
307 Self::ANON_PATH => Ok(Self::Anon),
308 other => Self::new(other).map_err(serde::de::Error::custom),
309 }
310 } else {
311 const KERNEL: u8 = LibraryNamespace::Kernel.tag();
312 const EXEC: u8 = LibraryNamespace::Exec.tag();
313 const ANON: u8 = LibraryNamespace::Anon.tag();
314 const USER: u8 = ANON + 1;
315
316 serde_untagged::UntaggedEnumVisitor::new()
317 .expecting("a valid section id")
318 .string(|s| Self::from_str(s).map_err(serde::de::Error::custom))
319 .u8(|tag| match tag {
320 KERNEL => Ok(Self::Kernel),
321 EXEC => Ok(Self::Exec),
322 ANON => Ok(Self::Anon),
323 USER => {
324 Err(serde::de::Error::custom("expected a user-defined library namespace"))
325 },
326 other => Err(serde::de::Error::invalid_value(
327 Unexpected::Unsigned(other as u64),
328 &"a valid library namespace variant",
329 )),
330 })
331 .seq(|mut seq| {
332 let tag = seq.next_element::<u8>()?.ok_or_else(|| {
333 serde::de::Error::invalid_length(0, &"a valid library namespace variant")
334 })?;
335 match tag {
336 KERNEL => Ok(Self::Kernel),
337 EXEC => Ok(Self::Exec),
338 ANON => Ok(Self::Anon),
339 USER => seq
340 .next_element::<&str>()?
341 .ok_or_else(|| {
342 serde::de::Error::invalid_length(
343 1,
344 &"a user-defined library namespace",
345 )
346 })
347 .and_then(|s| Self::new(s).map_err(serde::de::Error::custom)),
348 other => Err(serde::de::Error::invalid_value(
349 Unexpected::Unsigned(other as u64),
350 &"a valid library namespace variant",
351 )),
352 }
353 })
354 .deserialize(deserializer)
355 }
356 }
357}
358
359#[cfg(all(feature = "std", any(test, feature = "arbitrary")))]
360impl proptest::prelude::Arbitrary for LibraryNamespace {
361 type Parameters = ();
362
363 fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy {
364 use proptest::prelude::*;
365 prop_oneof![
366 Just(LibraryNamespace::Kernel),
367 Just(LibraryNamespace::Exec),
368 Just(LibraryNamespace::Anon),
369 prop::string::string_regex(r"[a-z][a-z0-9_]*")
370 .unwrap()
371 .prop_map(|s| { LibraryNamespace::User(Arc::from(s)) }),
372 ]
373 .boxed()
374 }
375
376 type Strategy = proptest::prelude::BoxedStrategy<Self>;
377}