miden_assembly_syntax/library/
namespace.rs1use alloc::{string::ToString, sync::Arc};
2use core::{
3 fmt,
4 str::{self, FromStr},
5};
6
7use miden_core::utils::{
8 ByteReader, ByteWriter, Deserializable, DeserializationError, Serializable,
9};
10use miden_debug_types::Span;
11use miden_utils_diagnostics::{Diagnostic, miette};
12
13use crate::{LibraryPath, ast::Ident};
14
15#[derive(Debug, thiserror::Error, Diagnostic)]
20pub enum LibraryNamespaceError {
21 #[error("invalid library namespace name: cannot be empty")]
22 #[diagnostic()]
23 Empty,
24 #[error("invalid library namespace name: too many characters")]
25 #[diagnostic()]
26 Length,
27 #[error(
28 "invalid character in library namespace: expected lowercase ascii-alphanumeric character or '_'"
29 )]
30 #[diagnostic()]
31 InvalidChars,
32 #[error("invalid library namespace name: must start with lowercase ascii-alphabetic character")]
33 #[diagnostic()]
34 InvalidStart,
35}
36
37#[derive(Default, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
39#[repr(u8)]
40pub enum LibraryNamespace {
41 Kernel = 0,
43 Exec,
45 #[default]
47 Anon,
48 User(Arc<str>),
50}
51
52impl LibraryNamespace {
55 pub const MAX_LENGTH: usize = u8::MAX as usize;
57
58 pub const KERNEL_PATH: &'static str = "$kernel";
60
61 pub const EXEC_PATH: &'static str = "$exec";
63
64 pub const ANON_PATH: &'static str = "$anon";
66}
67
68impl LibraryNamespace {
71 pub fn new<S>(source: S) -> Result<Self, LibraryNamespaceError>
73 where
74 S: AsRef<str>,
75 {
76 source.as_ref().parse()
77 }
78
79 pub fn from_ident_unchecked(name: Ident) -> Self {
83 match name.as_str() {
84 Self::KERNEL_PATH => Self::Kernel,
85 Self::EXEC_PATH => Self::Exec,
86 Self::ANON_PATH => Self::Anon,
87 _ => Self::User(name.into_inner()),
88 }
89 }
90
91 pub fn strip_path_prefix(path: &str) -> Result<(Self, &str), LibraryNamespaceError> {
94 match path.split_once("::") {
95 Some((ns, rest)) => ns.parse().map(|ns| (ns, rest)),
96 None => path.parse().map(|ns| (ns, "")),
97 }
98 }
99}
100
101impl LibraryNamespace {
104 pub fn is_reserved(&self) -> bool {
106 !matches!(self, Self::User(_))
107 }
108
109 pub fn validate(source: impl AsRef<str>) -> Result<(), LibraryNamespaceError> {
119 let source = source.as_ref();
120 if source.is_empty() {
121 return Err(LibraryNamespaceError::Empty);
122 }
123 if matches!(source, Self::KERNEL_PATH | Self::EXEC_PATH | Self::ANON_PATH) {
124 return Ok(());
125 }
126 if source.len() > Self::MAX_LENGTH {
127 return Err(LibraryNamespaceError::Length);
128 }
129 if !source.starts_with(|c: char| c.is_ascii_lowercase() && c.is_ascii_alphabetic()) {
130 return Err(LibraryNamespaceError::InvalidStart);
131 }
132 if !source.chars().all(|c| c.is_ascii_graphic() || c.is_alphanumeric()) {
133 return Err(LibraryNamespaceError::InvalidChars);
134 }
135 Ok(())
136 }
137}
138
139impl LibraryNamespace {
142 pub fn as_str(&self) -> &str {
144 match self {
145 Self::Kernel => Self::KERNEL_PATH,
146 Self::Exec => Self::EXEC_PATH,
147 Self::Anon => Self::ANON_PATH,
148 Self::User(path) => path,
149 }
150 }
151
152 pub fn as_refcounted_str(&self) -> Arc<str> {
154 match self {
155 Self::User(path) => path.clone(),
156 other => Arc::from(other.as_str().to_string().into_boxed_str()),
157 }
158 }
159
160 pub fn to_path(&self) -> LibraryPath {
162 LibraryPath::from(self.clone())
163 }
164
165 pub fn to_ident(&self) -> Ident {
167 Ident::from_raw_parts(Span::unknown(self.as_refcounted_str()))
168 }
169}
170
171impl core::ops::Deref for LibraryNamespace {
172 type Target = str;
173
174 fn deref(&self) -> &Self::Target {
175 self.as_str()
176 }
177}
178
179impl AsRef<str> for LibraryNamespace {
180 fn as_ref(&self) -> &str {
181 self.as_str()
182 }
183}
184
185impl fmt::Display for LibraryNamespace {
186 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
187 f.write_str(self.as_str())
188 }
189}
190
191impl FromStr for LibraryNamespace {
192 type Err = LibraryNamespaceError;
193
194 fn from_str(s: &str) -> Result<Self, Self::Err> {
195 match s {
196 Self::KERNEL_PATH => Ok(Self::Kernel),
197 Self::EXEC_PATH => Ok(Self::Exec),
198 Self::ANON_PATH => Ok(Self::Anon),
199 other => {
200 Self::validate(other)?;
201 Ok(Self::User(Arc::from(other.to_string().into_boxed_str())))
202 },
203 }
204 }
205}
206
207impl TryFrom<Ident> for LibraryNamespace {
208 type Error = LibraryNamespaceError;
209 fn try_from(ident: Ident) -> Result<Self, Self::Error> {
210 match ident.as_str() {
211 Self::KERNEL_PATH => Ok(Self::Kernel),
212 Self::EXEC_PATH => Ok(Self::Exec),
213 Self::ANON_PATH => Ok(Self::Anon),
214 other => Self::new(other),
215 }
216 }
217}
218
219impl Serializable for LibraryNamespace {
223 fn write_into<W: ByteWriter>(&self, target: &mut W) {
224 let bytes = self.as_bytes();
226 assert!(bytes.len() <= u8::MAX as usize, "namespace too long");
227
228 target.write_u8(bytes.len() as u8);
229 target.write_bytes(bytes);
230 }
231}
232
233impl Deserializable for LibraryNamespace {
234 fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
235 let num_bytes = source.read_u8()? as usize;
236 let name = source.read_slice(num_bytes)?;
237 let name =
238 str::from_utf8(name).map_err(|e| DeserializationError::InvalidValue(e.to_string()))?;
239 Self::new(name).map_err(|e| DeserializationError::InvalidValue(e.to_string()))
240 }
241}