1use alloc::{
2 borrow::Cow,
3 string::{String, ToString},
4 sync::Arc,
5 vec::Vec,
6};
7use core::{
8 fmt,
9 str::{self, FromStr},
10};
11
12use smallvec::smallvec;
13
14use crate::{
15 ast::{Ident, IdentError},
16 ByteReader, ByteWriter, Deserializable, DeserializationError, LibraryNamespace, Serializable,
17 Span,
18};
19
20#[derive(Debug, thiserror::Error)]
22pub enum PathError {
23 #[error("invalid library path: cannot be empty")]
24 Empty,
25 #[error("invalid library path component: cannot be empty")]
26 EmptyComponent,
27 #[error("invalid library path component: {0}")]
28 InvalidComponent(crate::ast::IdentError),
29 #[error("invalid library path: contains invalid utf8 byte sequences")]
30 InvalidUtf8,
31 #[error(transparent)]
32 InvalidNamespace(crate::library::LibraryNamespaceError),
33 #[error("cannot join a path with reserved name to other paths")]
34 UnsupportedJoin,
35}
36
37pub enum LibraryPathComponent<'a> {
42 Namespace(&'a LibraryNamespace),
44 Normal(&'a Ident),
46}
47
48impl<'a> LibraryPathComponent<'a> {
49 #[inline(always)]
51 pub fn as_str(&self) -> &'a str {
52 match self {
53 Self::Namespace(ns) => ns.as_str(),
54 Self::Normal(id) => id.as_str(),
55 }
56 }
57
58 #[inline]
60 pub fn to_ident(&self) -> Ident {
61 match self {
62 Self::Namespace(ns) => ns.to_ident(),
63 Self::Normal(id) => Ident::clone(id),
64 }
65 }
66}
67
68impl Eq for LibraryPathComponent<'_> {}
69
70impl PartialEq for LibraryPathComponent<'_> {
71 fn eq(&self, other: &Self) -> bool {
72 match (self, other) {
73 (Self::Namespace(a), Self::Namespace(b)) => a == b,
74 (Self::Normal(a), Self::Normal(b)) => a == b,
75 _ => false,
76 }
77 }
78}
79
80impl PartialEq<str> for LibraryPathComponent<'_> {
81 fn eq(&self, other: &str) -> bool {
82 self.as_ref().eq(other)
83 }
84}
85
86impl AsRef<str> for LibraryPathComponent<'_> {
87 fn as_ref(&self) -> &str {
88 match self {
89 Self::Namespace(ns) => ns.as_str(),
90 Self::Normal(ident) => ident.as_str(),
91 }
92 }
93}
94
95impl fmt::Display for LibraryPathComponent<'_> {
96 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
97 f.write_str(self.as_ref())
98 }
99}
100
101impl From<LibraryPathComponent<'_>> for Ident {
102 #[inline]
103 fn from(component: LibraryPathComponent<'_>) -> Self {
104 component.to_ident()
105 }
106}
107
108type Components = smallvec::SmallVec<[Ident; 1]>;
110
111#[derive(Default, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
116pub struct LibraryPath {
117 inner: Arc<LibraryPathInner>,
118}
119
120#[derive(Default, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
123struct LibraryPathInner {
124 ns: LibraryNamespace,
126 components: Components,
128}
129
130impl LibraryPath {
131 pub fn new(source: impl AsRef<str>) -> Result<Self, PathError> {
146 let source = source.as_ref();
147 if source.is_empty() {
148 return Err(PathError::Empty);
149 }
150
151 let mut parts = source.split("::");
153 let ns = parts
154 .next()
155 .ok_or(PathError::Empty)
156 .and_then(|part| LibraryNamespace::new(part).map_err(PathError::InvalidNamespace))?;
157
158 let mut components = Components::default();
160 parts.map(Ident::new).try_for_each(|part| {
161 part.map_err(PathError::InvalidComponent).map(|c| components.push(c))
162 })?;
163
164 Ok(Self::make(ns, components))
165 }
166
167 pub fn new_from_components<I>(ns: LibraryNamespace, components: I) -> Self
169 where
170 I: IntoIterator<Item = Ident>,
171 {
172 Self::make(ns, components.into_iter().collect())
173 }
174
175 #[inline]
176 fn make(ns: LibraryNamespace, components: Components) -> Self {
177 Self {
178 inner: Arc::new(LibraryPathInner { ns, components }),
179 }
180 }
181}
182
183impl LibraryPath {
185 #[allow(clippy::len_without_is_empty)]
187 pub fn len(&self) -> usize {
188 self.inner.components.iter().map(|c| c.len()).sum::<usize>()
189 + self.inner.ns.as_str().len()
190 + (self.inner.components.len() * 2)
191 }
192
193 pub fn byte_len(&self) -> usize {
195 self.inner.components.iter().map(|c| c.len()).sum::<usize>()
196 + self.inner.ns.as_str().len()
197 + (self.inner.components.len() * 2)
198 }
199
200 pub fn path(&self) -> Cow<'_, str> {
202 if self.inner.components.is_empty() {
203 Cow::Borrowed(self.inner.ns.as_str())
204 } else {
205 Cow::Owned(self.to_string())
206 }
207 }
208
209 pub fn namespace(&self) -> &LibraryNamespace {
211 &self.inner.ns
212 }
213
214 pub fn last(&self) -> &str {
216 self.last_component().as_str()
217 }
218
219 pub fn last_component(&self) -> LibraryPathComponent<'_> {
221 self.inner
222 .components
223 .last()
224 .map(LibraryPathComponent::Normal)
225 .unwrap_or_else(|| LibraryPathComponent::Namespace(&self.inner.ns))
226 }
227
228 pub fn num_components(&self) -> usize {
232 self.inner.components.len() + 1
233 }
234
235 pub fn components(&self) -> impl Iterator<Item = LibraryPathComponent> + '_ {
237 core::iter::once(LibraryPathComponent::Namespace(&self.inner.ns))
238 .chain(self.inner.components.iter().map(LibraryPathComponent::Normal))
239 }
240
241 pub fn is_kernel_path(&self) -> bool {
243 matches!(self.inner.ns, LibraryNamespace::Kernel)
244 }
245
246 pub fn is_exec_path(&self) -> bool {
248 matches!(self.inner.ns, LibraryNamespace::Exec)
249 }
250
251 pub fn is_anon_path(&self) -> bool {
253 matches!(self.inner.ns, LibraryNamespace::Anon)
254 }
255
256 pub fn starts_with(&self, other: &LibraryPath) -> bool {
258 let mut a = self.components();
259 let mut b = other.components();
260 loop {
261 match (a.next(), b.next()) {
262 (_, None) => break true,
264 (None, _) => break false,
266 (Some(a), Some(b)) => {
267 if a != b {
269 break false;
270 }
271 },
272 }
273 }
274 }
275}
276
277impl LibraryPath {
279 pub fn set_namespace(&mut self, ns: LibraryNamespace) {
281 let inner = Arc::make_mut(&mut self.inner);
282 inner.ns = ns;
283 }
284
285 pub fn join(&self, other: &Self) -> Result<Self, PathError> {
292 if other.inner.ns.is_reserved() {
293 return Err(PathError::UnsupportedJoin);
294 }
295
296 let mut path = self.clone();
297 {
298 let inner = Arc::make_mut(&mut path.inner);
299 inner.components.push(other.inner.ns.to_ident());
300 inner.components.extend(other.inner.components.iter().cloned());
301 }
302
303 Ok(path)
304 }
305
306 pub fn push(&mut self, component: impl AsRef<str>) -> Result<(), PathError> {
310 let component = component.as_ref().parse::<Ident>().map_err(PathError::InvalidComponent)?;
311 self.push_ident(component);
312 Ok(())
313 }
314
315 pub fn push_ident(&mut self, component: Ident) {
317 let inner = Arc::make_mut(&mut self.inner);
318 inner.components.push(component);
319 }
320
321 pub fn append<S>(&self, component: S) -> Result<Self, PathError>
325 where
326 S: AsRef<str>,
327 {
328 let mut path = self.clone();
329 path.push(component)?;
330 Ok(path)
331 }
332
333 pub fn append_ident(&self, component: Ident) -> Result<Self, PathError> {
337 let mut path = self.clone();
338 path.push_ident(component);
339 Ok(path)
340 }
341
342 pub fn prepend<S>(&self, component: S) -> Result<Self, PathError>
351 where
352 S: AsRef<str>,
353 {
354 let ns = component
355 .as_ref()
356 .parse::<LibraryNamespace>()
357 .map_err(PathError::InvalidNamespace)?;
358 let component = self.inner.ns.to_ident();
359 let mut components = smallvec![component];
360 components.extend(self.inner.components.iter().cloned());
361 Ok(Self::make(ns, components))
362 }
363
364 pub fn pop(&mut self) -> Option<Ident> {
366 let inner = Arc::make_mut(&mut self.inner);
367 inner.components.pop()
368 }
369
370 pub fn strip_last(&self) -> Option<Self> {
373 match self.inner.components.len() {
374 0 => None,
375 1 => Some(Self::make(self.inner.ns.clone(), smallvec![])),
376 _ => {
377 let ns = self.inner.ns.clone();
378 let mut components = self.inner.components.clone();
379 components.pop();
380 Some(Self::make(ns, components))
381 },
382 }
383 }
384
385 pub fn validate<S>(source: S) -> Result<usize, PathError>
390 where
391 S: AsRef<str>,
392 {
393 let source = source.as_ref();
394
395 let mut count = 0;
396 let mut components = source.split("::");
397
398 let ns = components.next().ok_or(PathError::Empty)?;
399 LibraryNamespace::validate(ns).map_err(PathError::InvalidNamespace)?;
400 count += 1;
401
402 for component in components {
403 validate_component(component)?;
404 count += 1;
405 }
406
407 Ok(count)
408 }
409
410 pub fn append_unchecked<S>(&self, component: S) -> Self
414 where
415 S: AsRef<str>,
416 {
417 let component = component.as_ref().to_string().into_boxed_str();
418 let component = Ident::new_unchecked(Span::unknown(Arc::from(component)));
419 let mut path = self.clone();
420 path.push_ident(component);
421 path
422 }
423}
424
425impl<'a> TryFrom<Vec<LibraryPathComponent<'a>>> for LibraryPath {
426 type Error = PathError;
427 fn try_from(iter: Vec<LibraryPathComponent<'a>>) -> Result<Self, Self::Error> {
428 let mut iter = iter.into_iter();
429 let ns = match iter.next() {
430 None => return Err(PathError::Empty),
431 Some(LibraryPathComponent::Namespace(ns)) => ns.clone(),
432 Some(LibraryPathComponent::Normal(ident)) => {
433 LibraryNamespace::try_from(ident.clone()).map_err(PathError::InvalidNamespace)?
434 },
435 };
436 let mut components = Components::default();
437 for component in iter {
438 match component {
439 LibraryPathComponent::Normal(ident) => components.push(ident.clone()),
440 LibraryPathComponent::Namespace(LibraryNamespace::User(name)) => {
441 components.push(Ident::new_unchecked(Span::unknown(name.clone())));
442 },
443 LibraryPathComponent::Namespace(_) => return Err(PathError::UnsupportedJoin),
444 }
445 }
446 Ok(Self::make(ns, components))
447 }
448}
449
450impl From<LibraryNamespace> for LibraryPath {
451 fn from(ns: LibraryNamespace) -> Self {
452 Self::make(ns, smallvec![])
453 }
454}
455
456impl From<LibraryPath> for String {
457 fn from(path: LibraryPath) -> Self {
458 path.to_string()
459 }
460}
461
462impl TryFrom<String> for LibraryPath {
463 type Error = PathError;
464
465 #[inline]
466 fn try_from(value: String) -> Result<Self, Self::Error> {
467 Self::new(value)
468 }
469}
470
471impl<'a> TryFrom<&'a str> for LibraryPath {
472 type Error = PathError;
473
474 #[inline]
475 fn try_from(value: &'a str) -> Result<Self, Self::Error> {
476 Self::new(value)
477 }
478}
479
480impl FromStr for LibraryPath {
481 type Err = PathError;
482
483 #[inline]
484 fn from_str(value: &str) -> Result<Self, Self::Err> {
485 Self::new(value)
486 }
487}
488
489impl Serializable for LibraryPath {
490 fn write_into<W: ByteWriter>(&self, target: &mut W) {
491 let len = self.byte_len();
492
493 target.write_u16(len as u16);
494 target.write_bytes(self.inner.ns.as_str().as_bytes());
495 for component in self.inner.components.iter() {
496 target.write_bytes(b"::");
497 target.write_bytes(component.as_str().as_bytes());
498 }
499 }
500}
501
502impl Deserializable for LibraryPath {
503 fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
504 let len = source.read_u16()? as usize;
505 let path = source.read_slice(len)?;
506 let path =
507 str::from_utf8(path).map_err(|e| DeserializationError::InvalidValue(e.to_string()))?;
508 Self::new(path).map_err(|e| DeserializationError::InvalidValue(e.to_string()))
509 }
510}
511
512impl fmt::Display for LibraryPath {
513 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
514 write!(f, "{}", self.inner.ns)?;
515 for component in self.inner.components.iter() {
516 write!(f, "::{component}")?;
517 }
518 Ok(())
519 }
520}
521
522fn validate_component(component: &str) -> Result<(), PathError> {
523 if component.is_empty() {
524 Err(PathError::EmptyComponent)
525 } else if component.len() > LibraryNamespace::MAX_LENGTH {
526 Err(PathError::InvalidComponent(IdentError::InvalidLength {
527 max: LibraryNamespace::MAX_LENGTH,
528 }))
529 } else {
530 Ident::validate(component).map_err(PathError::InvalidComponent)
531 }
532}
533
534#[cfg(test)]
539mod tests {
540 use vm_core::assert_matches;
541
542 use super::{super::LibraryNamespaceError, IdentError, LibraryPath, PathError};
543
544 #[test]
545 fn new_path() {
546 let path = LibraryPath::new("foo").unwrap();
547 assert_eq!(path.num_components(), 1);
548
549 let path = LibraryPath::new("foo::bar").unwrap();
550 assert_eq!(path.num_components(), 2);
551
552 let path = LibraryPath::new("foo::bar::baz").unwrap();
553 assert_eq!(path.num_components(), 3);
554
555 let path = LibraryPath::new("#exec::bar::baz").unwrap();
556 assert_eq!(path.num_components(), 3);
557
558 let path = LibraryPath::new("#sys::bar::baz").unwrap();
559 assert_eq!(path.num_components(), 3);
560 }
561
562 #[test]
563 fn new_path_fail() {
564 let path = LibraryPath::new("");
565 assert_matches!(path, Err(PathError::Empty));
566
567 let path = LibraryPath::new("::");
568 assert_matches!(path, Err(PathError::InvalidNamespace(LibraryNamespaceError::Empty)));
569
570 let path = LibraryPath::new("foo::");
571 assert_matches!(path, Err(PathError::InvalidComponent(IdentError::Empty)));
572
573 let path = LibraryPath::new("::foo");
574 assert_matches!(path, Err(PathError::InvalidNamespace(LibraryNamespaceError::Empty)));
575
576 let path = LibraryPath::new("foo::1bar");
577 assert_matches!(path, Err(PathError::InvalidComponent(IdentError::InvalidStart)));
578
579 let path = LibraryPath::new("foo::b@r");
580 assert_matches!(
581 path,
582 Err(PathError::InvalidComponent(IdentError::InvalidChars { ident: _ }))
583 );
584
585 let path = LibraryPath::new("#foo::bar");
586 assert_matches!(
587 path,
588 Err(PathError::InvalidNamespace(LibraryNamespaceError::InvalidStart))
589 );
590 }
591}