diman_unit_system 0.5.0

Internal procedural macros for diman.
Documentation
use proc_macro2::Span;
use syn::{
    bracketed, parenthesized,
    parse::{Parse, ParseBuffer, ParseStream},
    Error, Result, Token,
};

use crate::{
    parse::tokens,
    types::prefixes::{ExplicitPrefixes, MetricPrefixes, Prefix},
    types::{Alias, BaseAttribute, Symbol},
};

pub mod attribute_keywords {
    syn::custom_keyword!(base);
    syn::custom_keyword!(alias);
    syn::custom_keyword!(symbol);
    syn::custom_keyword!(metric_prefixes);
    syn::custom_keyword!(prefix);
}

pub mod prefix_attribute_keywords {
    syn::custom_keyword!(skip);
}

#[derive(PartialEq, Debug)]
pub enum AttributeName {
    Base,
    Alias,
    Symbol,
    MetricPrefixes,
    Prefix,
}

pub struct Attribute<'a> {
    name: AttributeName,
    span: Span,
    inner: Option<ParseBuffer<'a>>,
}

pub trait FromAttribute: Sized {
    fn is_correct_type(name: &AttributeName) -> bool {
        Self::correct_type() == *name
    }

    fn correct_type() -> AttributeName;

    fn from_attribute(attribute: &Attribute) -> Result<Self>;
}

pub trait ParseWithAttributes: Sized {
    fn parse_with_attributes(input: ParseStream, attributes: Attributes) -> Result<Self>;
}

pub struct Attributes<'a>(pub Vec<Attribute<'a>>);

impl<'a> Attributes<'a> {
    pub fn parse_all(input: ParseStream<'a>) -> Result<Self> {
        use attribute_keywords as attr_kw;
        let mut attributes = vec![];
        while input.peek(tokens::AttributeToken) {
            let _: tokens::AttributeToken = input.parse()?;
            let content;
            let _ = bracketed!(content in input);
            let span = content.span();
            let lookahead = content.lookahead1();
            let name = if lookahead.peek(attr_kw::base) {
                let _: attr_kw::base = content.parse()?;
                AttributeName::Base
            } else if lookahead.peek(attr_kw::alias) {
                let _: attr_kw::alias = content.parse()?;
                AttributeName::Alias
            } else if lookahead.peek(attr_kw::symbol) {
                let _: attr_kw::symbol = content.parse()?;
                AttributeName::Symbol
            } else if lookahead.peek(attr_kw::metric_prefixes) {
                let _: attr_kw::metric_prefixes = content.parse()?;
                AttributeName::MetricPrefixes
            } else if lookahead.peek(attr_kw::prefix) {
                let _: attr_kw::prefix = content.parse()?;
                AttributeName::Prefix
            } else {
                return Err(lookahead.error());
            };
            let inner = if content.peek(syn::token::Paren) {
                let inner;
                let _ = parenthesized!(inner in content);
                Some(inner)
            } else {
                None
            };
            attributes.push(Attribute { span, name, inner });
        }
        Ok(Self(attributes))
    }

    pub fn remove_all_of_type<T: FromAttribute>(&mut self) -> Result<Vec<T>> {
        let (ts, others): (Vec<_>, Vec<_>) =
            self.0.drain(..).partition(|a| T::is_correct_type(&a.name));
        self.0 = others;
        ts.into_iter().map(|t| T::from_attribute(&t)).collect()
    }

    pub fn remove_unique_of_type<T: FromAttribute>(&mut self) -> Result<Option<T>> {
        let (mut ts, others): (Vec<_>, Vec<_>) =
            self.0.drain(..).partition(|a| T::is_correct_type(&a.name));
        self.0 = others;
        if ts.is_empty() {
            Ok(None)
        } else if ts.len() == 1 {
            Ok(Some(T::from_attribute(&ts.remove(0))?))
        } else {
            Err(Error::new(
                ts.remove(1).span,
                format!("Multiple attributes of type {:?}.", T::correct_type()),
            ))
        }
    }

    pub fn check_none_left_over(mut self) -> Result<()> {
        if self.0.is_empty() {
            Ok(())
        } else {
            Err(Error::new(self.0.remove(0).span, "Unused attribute."))
        }
    }
}

impl Parse for Alias {
    fn parse(input: ParseStream) -> Result<Self> {
        Ok(Alias {
            name: input.parse()?,
        })
    }
}

impl<'a> Attribute<'a> {
    fn inner_or_err(&self) -> Result<&ParseBuffer> {
        self.inner
            .as_ref()
            .ok_or_else(|| Error::new(self.span, "Attribute expects arguments."))
    }
}

impl FromAttribute for Vec<Alias> {
    fn correct_type() -> AttributeName {
        AttributeName::Alias
    }

    fn from_attribute(attribute: &Attribute) -> Result<Self> {
        let inner = attribute.inner_or_err()?;
        let aliases = inner
            .parse_terminated(Alias::parse, Token![,])?
            .into_iter()
            .collect();
        Ok(aliases)
    }
}

impl FromAttribute for Symbol {
    fn correct_type() -> AttributeName {
        AttributeName::Symbol
    }

    fn from_attribute(attribute: &Attribute) -> Result<Self> {
        let inner = attribute.inner_or_err()?;
        let name = inner.parse()?;
        Ok(Symbol(name))
    }
}

impl FromAttribute for BaseAttribute {
    fn correct_type() -> AttributeName {
        AttributeName::Base
    }

    fn from_attribute(attribute: &Attribute) -> Result<Self> {
        let dimension = attribute.inner_or_err()?.parse()?;
        Ok(Self {
            dimension,
            attribute_span: attribute.span,
        })
    }
}

impl FromAttribute for MetricPrefixes {
    fn correct_type() -> AttributeName {
        AttributeName::MetricPrefixes
    }

    fn from_attribute(attr: &Attribute) -> Result<Self> {
        let skip = if let Some(inner) = &attr.inner {
            let lookahead = inner.lookahead1();
            if lookahead.peek(prefix_attribute_keywords::skip) {
                let _: prefix_attribute_keywords::skip = inner.parse()?;
                let _: Token![:] = inner.parse()?;
                inner
                    .parse_terminated(Prefix::parse, Token![,])?
                    .into_iter()
                    .collect()
            } else {
                vec![]
            }
        } else {
            vec![]
        };
        Ok(Self { skip })
    }
}

impl FromAttribute for ExplicitPrefixes {
    fn correct_type() -> AttributeName {
        AttributeName::Prefix
    }

    fn from_attribute(attr: &Attribute) -> Result<Self> {
        let inner = attr.inner_or_err()?;
        let prefixes = inner
            .parse_terminated(Prefix::parse, Token![,])?
            .into_iter()
            .collect();
        Ok(Self(prefixes))
    }
}