use proc_macro::TokenStream;
use proc_macro2::Span;
use syn;
use syn::{Type,parse,ItemStruct,Ident,Token};
use syn::token::Pound;
use syn::parse::{Parse,ParseStream};
use std::str::FromStr;
use quote::quote;
use ascii_basing::encoding::encode;
const ARGUMENT_ERROR_MESSAGE: &'static str = "The faux_array attribute should be given two arguments, the first of which should be a type and the second should be an integer";
struct Arguments {
field_count: u32,
field_type: Type,
}
impl Parse for Arguments {
fn parse(input: ParseStream) -> Result<Self,syn::Error> {
let inner_type: Type = input.parse()?;
Ok(Arguments {
field_count: 0,
field_type: inner_type,
})
}
}
#[proc_macro_attribute]
pub fn faux_array(args: TokenStream, actual: TokenStream) -> TokenStream {
let string_holder = args.to_string();
let mut string_args = string_holder.split(',');
let first_string = string_args.next().expect(format!("{}. No arguments were found",ARGUMENT_ERROR_MESSAGE).as_str());
let mut arguments: Arguments = parse(TokenStream::from_str(first_string).expect("The arguments given could not be converted back to a TokenStream after being converted to a String. Make sure your arguments list is also a valid Rust String and TokenStream")).expect(format!("{}. The first argument was {} , which could not be converted to a type",ARGUMENT_ERROR_MESSAGE,first_string).as_str());
arguments.field_count = string_args.next().expect(format!("{}. Only one argument was found",ARGUMENT_ERROR_MESSAGE).as_str()).trim().parse().expect(format!("{}. The second argument could not be parsed to a u32. Make sure the second argument is an integer that can be stored in a u32",ARGUMENT_ERROR_MESSAGE).as_str());
let build_length = usize::try_from(arguments.field_count).expect(format!("{}. The second argument was successfully parsed to a u32, but failed conversion to a usize integer. Make sure the second argument is less than or equal to {}",ARGUMENT_ERROR_MESSAGE,usize::MAX).as_str());
let structure: ItemStruct = parse(actual).expect("The faux_array attribute should only be attached to struct definitions");
let attributes = &structure.attrs;
let visibility = &structure.vis;
let name = &structure.ident;
let generics = &structure.generics;
let tipe = arguments.field_type;
let mut names: Vec<String> = Vec::with_capacity(build_length);
let hashtag: Pound = Token);
let mut idents: Vec<Ident> = Vec::with_capacity(build_length);
let mut copyscore = String::with_capacity(7);
let mut looper: u32 = 0;
while looper < arguments.field_count {
copyscore.push('_');
let new_name = encode(looper,None).expect("An unexpected error occurred. Please try again. If the error persists, contact me at richcreekbenjamin@gmail.com with a description of what is causing the bug");
copyscore.push_str(new_name.as_str());
names.push(new_name);
idents.push(Ident::new(©score,Span::call_site()));
looper += 1;
copyscore.clear();
}
quote! {
#(#attributes)*
#visibility struct #name #generics {
#(#hashtag[serde(rename = #names)]
#idents : #tipe),*
}
}.into()
}