use proc_macro;
use proc_macro2::{TokenStream, Literal, Group, Delimiter, Punct, Spacing};
use syn::{Token, parse::{Parse, ParseStream}};
use quote::TokenStreamExt;
#[proc_macro]
pub fn to_uppercase(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
let in_arg = syn::parse_macro_input!(input as syn::Ident);
let in_name = in_arg.to_string();
let out_name = in_name.to_uppercase();
let mut tokens = TokenStream::new();
tokens.append(Literal::string(&out_name));
tokens.into()
}
#[proc_macro]
pub fn map(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
let MapArgs { mut method_args, sql_args } = syn::parse_macro_input!(input as MapArgs);
let mut tokens = TokenStream::new();
if method_args.len() == 1 {
tokens.append(method_args.remove(0));
} else if method_args == sql_args {
let mut items = TokenStream::new();
if method_args.len() == 2 {
items.append_terminated(method_args, Punct::new(',', Spacing::Alone));
let unit = TokenStream::new();
items.append(Group::new(Delimiter::Parenthesis, unit));
} else {
items.append_separated(method_args, Punct::new(',', Spacing::Alone));
}
tokens.append(Group::new(Delimiter::Parenthesis, items));
} else {
let mut items = TokenStream::new();
for arg in method_args {
if !items.is_empty() {
items.append(Punct::new(',', Spacing::Alone));
}
let mut name_value = TokenStream::new();
name_value.append(Literal::string(arg.to_string().to_uppercase().as_str()));
name_value.append(Punct::new(',', Spacing::Alone));
name_value.append(arg);
items.append(Group::new(Delimiter::Parenthesis, name_value))
}
tokens.append(Group::new(Delimiter::Parenthesis, items));
}
tokens.into()
}
struct MapArgs {
method_args: Vec<syn::Ident>,
sql_args: Vec<syn::Ident>,
}
impl Parse for MapArgs {
fn parse(input: ParseStream) -> syn::Result<Self> {
let mut method_args = Vec::new();
while !input.peek(Token![=>]) {
let arg = input.parse()?;
method_args.push(arg);
}
let _ : Token![=>] = input.parse()?;
let mut sql_args = Vec::new();
while !input.is_empty() {
let _ : syn::LitStr = input.parse()?;
if input.is_empty() {
break;
}
let variant = input.lookahead1();
if variant.peek(Token![:]) {
let _ : Token![:] = input.parse()?;
} else if variant.peek(Token![#]) {
let _ : Token![#] = input.parse()?;
} else {
return Err(variant.error());
}
let arg = input.parse()?;
sql_args.push(arg);
}
Ok(Self { method_args, sql_args })
}
}