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