mod components;
mod join;
#[expect(clippy::module_inception)]
mod path;
mod path_buf;
pub use self::{
components::{Iter, PathComponent},
join::Join,
path::Path,
path_buf::PathBuf,
};
#[cfg(feature = "serde")]
use crate::debuginfo::Span;
use crate::diagnostics::{Diagnostic, miette};
#[derive(Debug, thiserror::Error)]
pub enum PathError {
#[error("invalid item path: cannot be empty")]
Empty,
#[error("invalid item path component: cannot be empty")]
EmptyComponent,
#[error("invalid item path component: {0}")]
InvalidComponent(crate::ast::IdentError),
#[error("invalid item path: contains invalid utf8 byte sequences")]
InvalidUtf8,
#[error("invalid item path: too long (max {max} bytes)")]
TooLong { max: usize },
#[error(transparent)]
InvalidNamespace(NamespaceError),
#[error("cannot join a path with reserved name to other paths")]
UnsupportedJoin,
#[error("'::' delimiter found where path component was expected")]
UnexpectedDelimiter,
#[error("path is missing a '::' delimiter between quoted/unquoted components")]
MissingPathSeparator,
#[error("quoted path component is missing a closing '\"'")]
UnclosedQuotedComponent,
}
#[derive(Debug, thiserror::Error, Diagnostic)]
pub enum NamespaceError {
#[error("invalid library namespace name: cannot be empty")]
#[diagnostic()]
Empty,
#[error("invalid library namespace name: too many characters")]
#[diagnostic()]
Length,
#[error(
"invalid character in library namespace: expected lowercase ascii-alphanumeric character or '_'"
)]
#[diagnostic()]
InvalidChars,
#[error("invalid library namespace name: must start with lowercase ascii-alphabetic character")]
#[diagnostic()]
InvalidStart,
}
pub trait StartsWith<Prefix: ?Sized> {
fn starts_with(&self, prefix: &Prefix) -> bool;
fn starts_with_exactly(&self, prefix: &Prefix) -> bool;
}
#[cfg(feature = "serde")]
pub fn serialize<P, S>(path: P, serializer: S) -> Result<S::Ok, S::Error>
where
P: AsRef<Path>,
S: serde::Serializer,
{
use serde::Serialize;
path.as_ref().serialize(serializer)
}
#[cfg(feature = "serde")]
pub fn deserialize<'de, P, D>(deserializer: D) -> Result<P, D::Error>
where
P: From<PathBuf>,
D: serde::Deserializer<'de>,
{
let path = <PathBuf as serde::Deserialize>::deserialize(deserializer)?;
Ok(P::from(path))
}
#[cfg(feature = "serde")]
pub fn deserialize_spanned<'de, P, D>(deserializer: D) -> Result<Span<P>, D::Error>
where
P: From<PathBuf>,
D: serde::Deserializer<'de>,
{
let path = <PathBuf as serde::Deserialize>::deserialize(deserializer)?;
Ok(Span::unknown(P::from(path)))
}
#[cfg(feature = "arbitrary")]
pub mod arbitrary {
use alloc::{sync::Arc, vec::Vec};
use proptest::{arbitrary::Arbitrary, collection::vec, prelude::*};
use super::*;
use crate::ast::{Ident, ident};
impl Arbitrary for PathBuf {
type Parameters = ();
fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy {
pathbuf_random_length(1).boxed()
}
type Strategy = BoxedStrategy<Self>;
}
prop_compose! {
fn components_any(min: u8, max: u8)
(components in vec(prop_oneof![
ident::arbitrary::ident_any_random_length(),
ident::arbitrary::bare_ident_any_random_length()
], (min as usize)..=(max as usize))) -> Vec<Ident> {
components
}
}
prop_compose! {
fn bare_components_any(min: u8, max: u8)
(components in vec(ident::arbitrary::bare_ident_any_random_length(), (min as usize)..=(max as usize))) -> Vec<Ident> {
components
}
}
prop_compose! {
pub fn pathbuf(min: u8, max: u8)
(components in components_any(min, max)) -> PathBuf {
let mut buf = PathBuf::default();
for component in components {
buf.push_component(&component);
}
buf
}
}
prop_compose! {
pub fn bare_pathbuf(min: u8, max: u8)
(components in bare_components_any(min, max)) -> PathBuf {
let mut buf = PathBuf::default();
for component in components {
buf.push_component(&component);
}
buf
}
}
prop_compose! {
pub fn constant_pathbuf(min: u8, max: u8)
(prefix in components_any(min, max), name in ident::arbitrary::const_ident_any_random_length()) -> PathBuf {
let mut buf = PathBuf::default();
for component in prefix {
buf.push_component(&component);
}
buf.push_component(&name);
buf
}
}
prop_compose! {
pub fn builtin_type_pathbuf()
(name in ident::arbitrary::builtin_type_any()) -> PathBuf {
PathBuf::from(name)
}
}
prop_compose! {
pub fn user_defined_type_pathbuf(min: u8, max: u8)
((name, prefix) in (ident::arbitrary::bare_ident_any_random_length(), components_any(min, max))) -> PathBuf {
let mut buf = PathBuf::default();
for component in prefix {
buf.push_component(&component);
}
buf.push_component(&name);
buf
}
}
prop_compose! {
pub fn type_pathbuf(min: u8, max: u8)
(path in prop_oneof![
1 => user_defined_type_pathbuf(min, max),
2 => builtin_type_pathbuf()
]) -> PathBuf {
path
}
}
prop_compose! {
pub fn pathbuf_random_length(min: u8)
(max in min..=core::cmp::max(min.saturating_add(1), 10))
(path in pathbuf(min, max)) -> PathBuf {
path
}
}
prop_compose! {
pub fn bare_pathbuf_random_length(min: u8)
(max in min..=core::cmp::max(min.saturating_add(1), 10))
(path in bare_pathbuf(min, max)) -> PathBuf {
path
}
}
prop_compose! {
pub fn constant_pathbuf_random_length(min: u8)
(max in min..=core::cmp::max(min.saturating_add(1), 10))
(path in constant_pathbuf(min, max)) -> PathBuf {
path
}
}
prop_compose! {
pub fn type_pathbuf_random_length(min: u8)
(max in min..=core::cmp::max(min.saturating_add(1), 10))
(path in type_pathbuf(min, max)) -> PathBuf {
path
}
}
prop_compose! {
pub fn user_defined_type_pathbuf_random_length(min: u8)
(max in min..=core::cmp::max(min.saturating_add(1), 10))
(path in user_defined_type_pathbuf(min, max)) -> PathBuf {
path
}
}
prop_compose! {
pub fn path_random_length(min: u8)
(path in pathbuf_random_length(min)) -> Arc<Path> {
path.into()
}
}
prop_compose! {
pub fn bare_path_random_length(min: u8)
(path in bare_pathbuf_random_length(min)) -> Arc<Path> {
path.into()
}
}
prop_compose! {
pub fn constant_path_random_length(min: u8)
(path in constant_pathbuf_random_length(min)) -> Arc<Path> {
path.into()
}
}
prop_compose! {
pub fn type_path_random_length(min: u8)
(path in type_pathbuf_random_length(min)) -> Arc<Path> {
path.into()
}
}
prop_compose! {
pub fn user_defined_type_path_random_length(min: u8)
(path in user_defined_type_pathbuf_random_length(min)) -> Arc<Path> {
path.into()
}
}
}