extern crate proc_macro;
use proc_macro::TokenStream;
use proc_macro2::Span;
use quote::quote;
use std::collections::HashMap;
use syn::{parse::{Parse, ParseStream, Result}, Token, punctuated::Punctuated,
fold::Fold, Expr, ExprAssign, Ident, LitInt, Lit, parse_macro_input,
ItemStruct, Type, Field, parse_quote};
type MapType = HashMap<Ident, LitInt>;
struct Args {
size_map: MapType,
typ: Ident,
}
const ERRMSG: &str = "Must specify an Ident=Int or typ=Structname";
impl Parse for Args {
fn parse(input: ParseStream) -> Result<Self> {
let vars = Punctuated::<ExprAssign, Token![,]>::parse_terminated(input)?;
let mut size_map = MapType::new();
let mut typ = Ident::new("ArrayString", Span::mixed_site());
for var in vars.into_iter() {
match (&*var.left, &*var.right) {
(Expr::Path(p), Expr::Lit(v)) => {
let key = p.path.get_ident().unwrap();
if let Lit::Int(num) = &v.lit {
size_map.insert(key.clone(), num.clone());
} else {
return Err(input.error(ERRMSG));
}
},
(Expr::Path(p), Expr::Path(v)) => {
let key = p.path.get_ident().unwrap();
if key.to_string() != "typ" {
return Err(input.error(ERRMSG));
}
if let Some(val) = v.path.get_ident() {
typ = val.clone();
} else {
return Err(input.error(ERRMSG));
}
}
(_, _) => {
return Err(input.error(ERRMSG));
}
}
}
Ok(Args { size_map, typ })
}
}
impl Fold for Args {
fn fold_field(&mut self, input: Field) -> syn::Field {
if let Some(key) = &input.ident {
let typ = &self.typ;
if let Some(num) = self.size_map.get(key) {
if let Type::Path(p) = &input.ty {
if p.path.is_ident("String") {
return Field {
attrs: input.attrs,
vis: input.vis,
mutability: input.mutability,
ident: input.ident,
colon_token: input.colon_token,
ty: parse_quote!{#typ::<#num>},
};
}
}
}
}
input
}
}
#[proc_macro_attribute]
pub fn fixed(args: TokenStream, input: TokenStream) -> TokenStream {
let mut args = parse_macro_input!(args as Args);
let input = parse_macro_input!(input as ItemStruct);
let output = args.fold_item_struct(input);
proc_macro::TokenStream::from(quote!(#output))
}