use crate::ast::prelude::*;
#[test]
#[cfg(not(miri))]
fn ast_parse() {
rt::<ast::Path>("foo::bar");
rt::<ast::Path>("Self::bar");
rt::<ast::Path>("self::bar");
rt::<ast::Path>("crate::bar");
rt::<ast::Path>("super::bar");
rt::<ast::Path>("HashMap::<Foo, Bar>");
rt::<ast::Path>("super::HashMap::<Foo, Bar>");
}
#[derive(Debug, TryClone, PartialEq, Eq, Parse, ToTokens, Spanned)]
#[non_exhaustive]
pub struct Path {
#[rune(iter)]
pub global: Option<T![::]>,
pub first: PathSegment,
#[rune(iter)]
pub rest: Vec<(T![::], PathSegment)>,
#[rune(iter)]
pub trailing: Option<T![::]>,
#[rune(skip)]
pub(crate) id: ItemId,
}
impl Path {
pub(crate) fn as_kind(&self) -> Option<PathKind<'_>> {
if self.rest.is_empty() && self.trailing.is_none() && self.global.is_none() {
match &self.first {
PathSegment::SelfValue(..) => Some(PathKind::SelfValue),
PathSegment::Ident(ident) => Some(PathKind::Ident(ident)),
_ => None,
}
} else {
None
}
}
pub(crate) fn try_as_ident(&self) -> Option<&ast::Ident> {
if self.rest.is_empty() && self.trailing.is_none() && self.global.is_none() {
self.first.try_as_ident()
} else {
None
}
}
pub(crate) fn try_as_ident_generics(
&self,
) -> Option<(
&ast::Ident,
Option<&ast::AngleBracketed<PathSegmentExpr, T![,]>>,
)> {
if self.trailing.is_none() && self.global.is_none() {
if let Some(ident) = self.first.try_as_ident() {
let generics = if let [(_, ast::PathSegment::Generics(generics))] = &self.rest[..] {
Some(generics)
} else {
None
};
return Some((ident, generics));
}
}
None
}
}
impl Peek for Path {
fn peek(p: &mut Peeker<'_>) -> bool {
matches!(p.nth(0), K![::]) || PathSegment::peek(p)
}
}
impl IntoExpectation for &Path {
fn into_expectation(self) -> Expectation {
Expectation::Description("path")
}
}
impl Resolve<'_> for Path {
type Output = Box<str>;
fn resolve(&self, cx: ResolveContext<'_>) -> Result<Self::Output> {
let mut buf = String::new();
if self.global.is_some() {
buf.try_push_str("::")?;
}
match &self.first {
PathSegment::SelfType(_) => {
buf.try_push_str("Self")?;
}
PathSegment::SelfValue(_) => {
buf.try_push_str("self")?;
}
PathSegment::Ident(ident) => {
buf.try_push_str(ident.resolve(cx)?)?;
}
PathSegment::Crate(_) => {
buf.try_push_str("crate")?;
}
PathSegment::Super(_) => {
buf.try_push_str("super")?;
}
PathSegment::Generics(_) => {
buf.try_push_str("<*>")?;
}
}
for (_, segment) in &self.rest {
buf.try_push_str("::")?;
match segment {
PathSegment::SelfType(_) => {
buf.try_push_str("Self")?;
}
PathSegment::SelfValue(_) => {
buf.try_push_str("self")?;
}
PathSegment::Ident(ident) => {
buf.try_push_str(ident.resolve(cx)?)?;
}
PathSegment::Crate(_) => {
buf.try_push_str("crate")?;
}
PathSegment::Super(_) => {
buf.try_push_str("super")?;
}
PathSegment::Generics(_) => {
buf.try_push_str("<*>")?;
}
}
}
if self.trailing.is_some() {
buf.try_push_str("::")?;
}
Ok(buf.try_into_boxed_str()?)
}
}
#[derive(Debug, TryClone, Clone, Copy, PartialEq, Eq)]
#[try_clone(copy)]
#[non_exhaustive]
pub enum PathKind<'a> {
SelfValue,
Ident(&'a ast::Ident),
}
#[derive(Debug, TryClone, PartialEq, Eq, ToTokens, Spanned)]
#[non_exhaustive]
pub enum PathSegment {
SelfType(T![Self]),
SelfValue(T![self]),
Ident(ast::Ident),
Crate(T![crate]),
Super(T![super]),
Generics(ast::AngleBracketed<PathSegmentExpr, T![,]>),
}
impl PathSegment {
pub(crate) fn try_as_ident(&self) -> Option<&ast::Ident> {
if let PathSegment::Ident(ident) = self {
Some(ident)
} else {
None
}
}
}
impl IntoExpectation for PathSegment {
fn into_expectation(self) -> Expectation {
Expectation::Description("path segment")
}
}
impl Parse for PathSegment {
fn parse(p: &mut Parser<'_>) -> Result<Self> {
let segment = match p.nth(0)? {
K![Self] => Self::SelfType(p.parse()?),
K![self] => Self::SelfValue(p.parse()?),
K![ident] => Self::Ident(p.parse()?),
K![crate] => Self::Crate(p.parse()?),
K![super] => Self::Super(p.parse()?),
K![<] => Self::Generics(p.parse()?),
_ => {
return Err(compile::Error::expected(p.tok_at(0)?, "path segment"));
}
};
Ok(segment)
}
}
impl Peek for PathSegment {
fn peek(p: &mut Peeker<'_>) -> bool {
matches!(
p.nth(0),
K![<] | K![Self] | K![self] | K![crate] | K![super] | K![ident]
)
}
}
#[derive(Debug, TryClone, PartialEq, Eq, ToTokens, Spanned)]
#[non_exhaustive]
pub struct PathSegmentExpr {
pub expr: ast::Expr,
}
impl Parse for PathSegmentExpr {
fn parse(p: &mut Parser<'_>) -> Result<Self> {
let expr = ast::Expr::parse_with(
p,
ast::expr::NOT_EAGER_BRACE,
ast::expr::NOT_EAGER_BINARY,
ast::expr::NOT_CALLABLE,
)?;
Ok(Self { expr })
}
}
impl Peek for PathSegmentExpr {
fn peek(p: &mut Peeker<'_>) -> bool {
ast::Expr::peek(p)
}
}