miden_assembly/library/
namespace.rs1use alloc::{string::ToString, sync::Arc};
2use core::{
3 fmt,
4 str::{self, FromStr},
5};
6
7use crate::{
8 ast::Ident, diagnostics::Diagnostic, ByteReader, ByteWriter, Deserializable,
9 DeserializationError, LibraryPath, Serializable, Span,
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("invalid character in library namespace: expected lowercase ascii-alphanumeric character or '_'")]
25 #[diagnostic()]
26 InvalidChars,
27 #[error(
28 "invalid library namespace name: must start with lowercase ascii-alphabetic character"
29 )]
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> {
108 let source = source.as_ref();
109 if source.is_empty() {
110 return Err(LibraryNamespaceError::Empty);
111 }
112 if matches!(source, Self::KERNEL_PATH | Self::EXEC_PATH | Self::ANON_PATH) {
113 return Ok(());
114 }
115 if source.len() > Self::MAX_LENGTH {
116 return Err(LibraryNamespaceError::Length);
117 }
118 if !source.starts_with(|c: char| c.is_ascii_lowercase() && c.is_ascii_alphabetic()) {
119 return Err(LibraryNamespaceError::InvalidStart);
120 }
121 if !source.chars().all(|c| c.is_ascii_alphanumeric() || matches!(c, '_')) {
122 return Err(LibraryNamespaceError::InvalidChars);
123 }
124 Ok(())
125 }
126}
127
128impl LibraryNamespace {
131 pub fn as_str(&self) -> &str {
133 match self {
134 Self::Kernel => Self::KERNEL_PATH,
135 Self::Exec => Self::EXEC_PATH,
136 Self::Anon => Self::ANON_PATH,
137 Self::User(ref path) => path,
138 }
139 }
140
141 pub fn as_refcounted_str(&self) -> Arc<str> {
143 match self {
144 Self::User(ref path) => path.clone(),
145 other => Arc::from(other.as_str().to_string().into_boxed_str()),
146 }
147 }
148
149 pub fn to_path(&self) -> LibraryPath {
151 LibraryPath::from(self.clone())
152 }
153
154 pub fn to_ident(&self) -> Ident {
156 Ident::new_unchecked(Span::unknown(self.as_refcounted_str()))
157 }
158}
159
160impl core::ops::Deref for LibraryNamespace {
161 type Target = str;
162
163 fn deref(&self) -> &Self::Target {
164 self.as_str()
165 }
166}
167
168impl AsRef<str> for LibraryNamespace {
169 fn as_ref(&self) -> &str {
170 self.as_str()
171 }
172}
173
174impl fmt::Display for LibraryNamespace {
175 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
176 f.write_str(self.as_str())
177 }
178}
179
180impl FromStr for LibraryNamespace {
181 type Err = LibraryNamespaceError;
182
183 fn from_str(s: &str) -> Result<Self, Self::Err> {
184 match s {
185 Self::KERNEL_PATH => Ok(Self::Kernel),
186 Self::EXEC_PATH => Ok(Self::Exec),
187 Self::ANON_PATH => Ok(Self::Anon),
188 other => {
189 Self::validate(other)?;
190 Ok(Self::User(Arc::from(other.to_string().into_boxed_str())))
191 },
192 }
193 }
194}
195
196impl TryFrom<Ident> for LibraryNamespace {
197 type Error = LibraryNamespaceError;
198 fn try_from(ident: Ident) -> Result<Self, Self::Error> {
199 match ident.as_str() {
200 Self::KERNEL_PATH => Ok(Self::Kernel),
201 Self::EXEC_PATH => Ok(Self::Exec),
202 Self::ANON_PATH => Ok(Self::Anon),
203 other => Self::new(other),
204 }
205 }
206}
207
208impl Serializable for LibraryNamespace {
212 fn write_into<W: ByteWriter>(&self, target: &mut W) {
213 let bytes = self.as_bytes();
215 assert!(bytes.len() <= u8::MAX as usize, "namespace too long");
216
217 target.write_u8(bytes.len() as u8);
218 target.write_bytes(bytes);
219 }
220}
221
222impl Deserializable for LibraryNamespace {
223 fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
224 let num_bytes = source.read_u8()? as usize;
225 let name = source.read_slice(num_bytes)?;
226 let name =
227 str::from_utf8(name).map_err(|e| DeserializationError::InvalidValue(e.to_string()))?;
228 Self::new(name).map_err(|e| DeserializationError::InvalidValue(e.to_string()))
229 }
230}