use crate::utils::{Cat6, Colon, Equals};
use proc_macro::TokenStream;
use proc_macro2::TokenStream as TokenStream2;
use syn::{
DeriveInput, Error as SynError, Expr, Ident, Type, Visibility,
parse::{Parse, ParseStream},
};
pub struct ConstAssoc {
visibility: Visibility,
ident: Ident,
ty: Type,
expr: Expr,
}
impl ConstAssoc {
const ATTRIBUTE_NAME: &str = "const_assoc";
fn from_derive_input(input: &DeriveInput) -> Result<Vec<Self>, SynError> {
let mut const_assocs = Vec::new();
for attribute in &input.attrs {
if attribute.path().is_ident(Self::ATTRIBUTE_NAME) {
const_assocs.push(attribute.parse_args::<Self>()?);
}
}
Ok(const_assocs)
}
fn associated_item(&self) -> TokenStream2 {
let Self {
visibility,
ident,
ty,
expr,
} = self;
quote::quote! {
#visibility const #ident: #ty = #expr;
}
}
fn derive_impl(input: &DeriveInput) -> Result<TokenStream2, SynError> {
let DeriveInput { ident, generics, .. } = input;
let const_assocs = Self::from_derive_input(input)?;
let (impl_generics, input_generics, input_where_clause) = generics.split_for_impl();
let associated_items = const_assocs.iter().map(Self::associated_item);
let impl_block_token_stream = quote::quote! {
impl #impl_generics #ident #input_generics #input_where_clause {
#(#associated_items)*
}
};
Ok(impl_block_token_stream)
}
pub fn derive(input_token_stream: TokenStream) -> TokenStream {
let input = syn::parse_macro_input!(input_token_stream);
Self::derive_impl(&input)
.unwrap_or_else(SynError::into_compile_error)
.into()
}
}
impl Parse for ConstAssoc {
fn parse(parse_stream: ParseStream) -> Result<Self, SynError> {
let (visibility, ident, _colon, ty, _equals, expr) = parse_stream
.parse::<Cat6<Visibility, Ident, Colon, Type, Equals, Expr>>()?
.into_tuple();
let const_assoc = Self {
visibility,
ident,
ty,
expr,
};
Ok(const_assoc)
}
}