inktree 0.1.0

An incremental syntax tree engine for fast, expressive language tooling in Rust
Documentation
use proc_macro2::TokenStream;
use quote::quote;
use syn::{Ident, Lit, LitStr, MetaList};

use crate::{
    derive::{
        attributes::allowed::ALLOWED_STATIC_TOKEN,
        parser::FromMeta,
        properties::{Operator, Property, PropertyKind, try_handle_extra},
    },
    language::{ElementError, Language, LanguageElement},
};

pub struct StaticToken {
    pub text: String,
    name: Ident,
    is_extra: bool,
}

impl StaticToken {
    fn from_lit(lit: &Lit, name: Ident) -> Result<Self, ElementError> {
        let text = match lit {
            Lit::Str(str) => str.value(),
            Lit::ByteStr(_) => todo!("support byte str"),
            _ => todo!("lit type error"),
        };
        Ok(Self {
            text,
            name,
            is_extra: false,
        })
    }
}

impl FromMeta for StaticToken {
    fn from_list(list: &MetaList, name: Option<&Ident>) -> Result<Self, ElementError> {
        let lit: LitStr = list.parse_args()?;
        Ok(Self {
            text: lit.value(),
            name: name.expect("todo name option").clone(),
            is_extra: false,
        }
        .into())
    }

    fn from_name_value(
        name_value: &syn::MetaNameValue,
        name: Option<&Ident>,
    ) -> Result<Self, ElementError> {
        match &name_value.value {
            syn::Expr::Lit(expr_lit) => {
                Ok(Self::from_lit(&expr_lit.lit, name.expect("todo name option").clone())?.into())
            }
            _ => Err(syn::Error::new_spanned(
                &name_value.value,
                "todo better text expr type error",
            ))?,
        }
    }
}

impl LanguageElement for StaticToken {
    fn codegen(&self, language: &Language) -> Result<TokenStream, ElementError> {
        let text = &self.text;
        let ident = &self.name;
        let lang_ident = &language.ident;

        if language.extras.is_empty() || self.is_extra {
            Ok(quote! {
               inktree::static_token!(#lang_ident::#ident,#text);
            })
        } else {
            Ok(quote! {
               inktree::static_token!(#lang_ident::#ident,#text,has_extras);
            })
        }
    }

    fn allowed(&self) -> &'static [PropertyKind] {
        ALLOWED_STATIC_TOKEN
    }

    fn name(&self) -> &Ident {
        &self.name
    }

    fn build(
        &mut self,
        properties: &Vec<Property>,
        language: &mut Language,
    ) -> Result<(), ElementError> {
        properties.iter().for_each(|prop| {
            if let Some(kind) = prop.try_as_operator() {
                language.operators.push(Operator {
                    kind: kind,
                    ident: self.name.clone(),
                });
            } else {
                self.is_extra = try_handle_extra(&self.name, prop, language);
            }
        });

        Ok(())
    }
}