rune 0.9.0

An embeddable dynamic programming language for Rust.
Documentation
use crate::ast;
use crate::{
    Parse, ParseError, Parser, Peek, Peeker, Resolve, ResolveError, ResolveErrorKind, ResolveOwned,
    Spanned, Storage, ToTokens,
};
use runestick::{Source, Span};
use std::borrow::Cow;

/// An identifier, like `foo` or `Hello`.".
#[derive(Debug, Clone, Copy, PartialEq, Eq, ToTokens, Spanned)]
pub struct Ident {
    /// The kind of the identifier.
    pub token: ast::Token,
    /// The kind of the identifier.
    #[rune(skip)]
    pub source: ast::StringSource,
}

impl Ident {
    /// Construct a new identifier from the given string from inside of a macro
    /// context.
    ///
    /// This constructor must only be used inside of a macro.
    ///
    /// # Panics
    ///
    /// This will panic if it's called outside of a macro context.
    pub fn new(ident: &str) -> Self {
        crate::macros::current_context(|ctx| Self::new_with(ident, ctx.macro_span(), ctx.storage()))
    }

    /// Construct a new identifier from the given string.
    ///
    /// This does not panic when called outside of a macro but requires access
    /// to a `span` and `storage`.
    pub(crate) fn new_with(ident: &str, span: Span, storage: &Storage) -> ast::Ident {
        let id = storage.insert_str(ident);
        let source = ast::StringSource::Synthetic(id);

        ast::Ident {
            token: ast::Token {
                span,
                kind: ast::Kind::Ident(source),
            },
            source,
        }
    }
}

impl Parse for Ident {
    fn parse(parser: &mut Parser<'_>) -> Result<Self, ParseError> {
        let token = parser.next()?;

        match token.kind {
            ast::Kind::Ident(source) => Ok(Self { token, source }),
            _ => Err(ParseError::expected(&token, "ident")),
        }
    }
}

impl Peek for Ident {
    fn peek(p: &mut Peeker<'_>) -> bool {
        matches!(p.nth(0), K![ident])
    }
}

impl<'a> Resolve<'a> for Ident {
    type Output = Cow<'a, str>;

    fn resolve(&self, storage: &Storage, source: &'a Source) -> Result<Cow<'a, str>, ResolveError> {
        let span = self.token.span();

        match self.source {
            ast::StringSource::Text => {
                let ident = source
                    .source(span)
                    .ok_or_else(|| ResolveError::new(span, ResolveErrorKind::BadSlice))?;

                Ok(Cow::Borrowed(ident))
            }
            ast::StringSource::Synthetic(id) => {
                let ident = storage.get_string(id).ok_or_else(|| {
                    ResolveError::new(span, ResolveErrorKind::BadSyntheticId { kind: "label", id })
                })?;

                Ok(Cow::Owned(ident))
            }
            ast::StringSource::BuiltIn(builtin) => Ok(Cow::Borrowed(builtin.as_str())),
        }
    }
}

impl ResolveOwned for Ident {
    type Owned = String;

    fn resolve_owned(
        &self,
        storage: &Storage,
        source: &Source,
    ) -> Result<Self::Owned, ResolveError> {
        let output = self.resolve(storage, source)?;

        match output {
            Cow::Borrowed(borrowed) => Ok(borrowed.to_owned()),
            Cow::Owned(owned) => Ok(owned),
        }
    }
}