I DID STUFF, GOD A PROC MACRO AND SHIT
This commit is contained in:
commit
22319e84a1
28 changed files with 3101 additions and 0 deletions
258
tavern_macros/src/lib.rs
Normal file
258
tavern_macros/src/lib.rs
Normal file
|
@ -0,0 +1,258 @@
|
|||
use proc_macro2::{Delimiter, Group, Span, TokenStream};
|
||||
use quote::{quote, ToTokens, TokenStreamExt};
|
||||
use syn::{parse_macro_input, DeriveInput, Ident};
|
||||
|
||||
#[proc_macro_derive(CharacterSheet, attributes(Input, Field, FieldExpr, Seperator))]
|
||||
pub fn derive(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
|
||||
let DeriveInput { ident, data, .. } = parse_macro_input!(input as DeriveInput);
|
||||
let mut output = quote! {
|
||||
impl CharacterSheet for #ident
|
||||
};
|
||||
|
||||
// Create inputs impl
|
||||
let mut impls = impl_inputs(&data);
|
||||
impls.extend(impl_fields(&data));
|
||||
impls.extend(impl_display(&data));
|
||||
impls.extend(impl_get(&data));
|
||||
impls.extend(impl_set(&data));
|
||||
output.append(Group::new(Delimiter::Brace, impls));
|
||||
|
||||
|
||||
output.into()
|
||||
}
|
||||
|
||||
fn get_type_ident(ty: &syn::Type) -> Option<Ident> {
|
||||
match ty {
|
||||
syn::Type::Path(p) => {
|
||||
if p.path.is_ident("String") {
|
||||
Some(Ident::new("Text", Span::call_site()))
|
||||
} else if p.path.is_ident("i32") {
|
||||
Some(Ident::new("Number", Span::call_site()))
|
||||
} else if p.path.is_ident("bool") {
|
||||
Some(Ident::new("Bool", Span::call_site()))
|
||||
} else { None }
|
||||
}
|
||||
_ => panic!("Invalid data type"),
|
||||
}
|
||||
}
|
||||
fn get_type_ident_set(ty: &syn::Type) -> Option<Ident> {
|
||||
match ty {
|
||||
syn::Type::Path(p) => {
|
||||
if p.path.is_ident("String") {
|
||||
Some(Ident::new("as_text", Span::call_site()))
|
||||
} else if p.path.is_ident("i32") {
|
||||
Some(Ident::new("as_num", Span::call_site()))
|
||||
} else if p.path.is_ident("bool") {
|
||||
Some(Ident::new("as_bool", Span::call_site()))
|
||||
} else { None }
|
||||
}
|
||||
_ => panic!("Invalid data type"),
|
||||
}
|
||||
}
|
||||
|
||||
fn impl_inputs(data: &syn::Data) -> TokenStream {
|
||||
let mut output = TokenStream::new();
|
||||
output.extend(quote! {
|
||||
fn inputs(&self) -> Vec<(String, EntryType)>
|
||||
});
|
||||
let mut items = TokenStream::new();
|
||||
if let syn::Data::Struct(ds) = data {
|
||||
if let syn::Fields::Named(fs) = &ds.fields {
|
||||
for f in &fs.named {
|
||||
let name = f.ident.clone().unwrap();
|
||||
let attr = f.attrs.iter().find(|a| a.path.is_ident("Input"));
|
||||
if let Some(attr) = attr {
|
||||
let t = get_type_ident(&f.ty).expect(&format!("Invalid type for input: {}", name));
|
||||
let arg: syn::LitStr = attr.parse_args().expect("No arguments supplied for Input attribute, usage: `Input(\"Name\")`");
|
||||
items.extend(quote! {
|
||||
(#arg.to_string(), EntryType::#t(self.#name.clone())),
|
||||
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
let mut vec = quote! { Vec::from };
|
||||
vec.append(Group::new(Delimiter::Parenthesis, Group::new(Delimiter::Bracket, items).to_token_stream()));
|
||||
output.append(Group::new(Delimiter::Brace, vec));
|
||||
|
||||
output
|
||||
}
|
||||
|
||||
fn impl_fields(data: &syn::Data) -> TokenStream {
|
||||
let mut output = TokenStream::new();
|
||||
output.extend(quote! {
|
||||
fn fields(&self) -> Vec<(String, EntryType)>
|
||||
});
|
||||
|
||||
let mut items = TokenStream::new();
|
||||
if let syn::Data::Struct(ds) = data {
|
||||
if let syn::Fields::Named(fs) = &ds.fields {
|
||||
for f in &fs.named {
|
||||
let name = f.ident.clone().unwrap();
|
||||
let input_attr = f.attrs.iter().find(|a| a.path.is_ident("Input"));
|
||||
if let Some(attr) = input_attr {
|
||||
let t = get_type_ident(&f.ty).expect(&format!("Invalid type for input: {}", name));
|
||||
let arg: syn::LitStr = attr.parse_args().expect("No arguments supplied for Input attribute, usage: `Input(\"Name\")`");
|
||||
items.extend(quote! {
|
||||
(#arg.to_string(), EntryType::#t(self.#name.clone())),
|
||||
|
||||
})
|
||||
}
|
||||
let field_attr = f.attrs.iter().find(|a| a.path.is_ident("Field"));
|
||||
if let Some(attr) = field_attr {
|
||||
let t = get_type_ident(&f.ty).expect(&format!("Invalid type for field: {}", name));
|
||||
let arg: syn::LitStr = attr.parse_args().expect("Invalid arguments for Field attribute");
|
||||
let field_expr = f.attrs.iter().find(|a| a.path.is_ident("FieldExpr"));
|
||||
if let Some(exp) = field_expr {
|
||||
let exp: syn::Expr = exp.parse_args().expect("Invalid expression");
|
||||
items.extend(quote! {
|
||||
(#arg.to_string(), EntryType::Number(#exp)),
|
||||
})
|
||||
}
|
||||
else {
|
||||
// No expression, guess we just push this and nothing special (should prob be an input but mehhh)
|
||||
items.extend(quote! {
|
||||
(#arg.to_string(), EntryType::#t(self.#name.clone())),
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
let mut vec = quote! { Vec::from };
|
||||
vec.append(Group::new(Delimiter::Parenthesis, Group::new(Delimiter::Bracket, items).to_token_stream()));
|
||||
output.append(Group::new(Delimiter::Brace, vec));
|
||||
|
||||
output
|
||||
}
|
||||
|
||||
fn impl_get(data: &syn::Data) -> TokenStream {
|
||||
let mut output = TokenStream::new();
|
||||
output.extend(quote! {
|
||||
fn get(&self, entry: &str) -> Option<EntryType>
|
||||
});
|
||||
let mut match_hands = TokenStream::new();
|
||||
if let syn::Data::Struct(ds) = data {
|
||||
if let syn::Fields::Named(fs) = &ds.fields {
|
||||
for f in &fs.named {
|
||||
let name = f.ident.clone().unwrap();
|
||||
let input_attr = f.attrs.iter().find(|a| a.path.is_ident("Input"));
|
||||
if let Some(attr) = input_attr {
|
||||
let t = get_type_ident(&f.ty).expect(&format!("Invalid type for input: {}", name));
|
||||
let arg: syn::LitStr = attr.parse_args().expect("No arguments supplied for Input attribute, usage: `Input(\"Name\")`");
|
||||
match_hands.extend(quote! {
|
||||
#arg => Some(EntryType::#t(self.#name.clone())),
|
||||
})
|
||||
}
|
||||
let field_attr = f.attrs.iter().find(|a| a.path.is_ident("Field"));
|
||||
if let Some(attr) = field_attr {
|
||||
let t = get_type_ident(&f.ty).expect(&format!("Invalid type for field: {}", name));
|
||||
let arg: syn::LitStr = attr.parse_args().expect("Invalid arguments for Field attribute");
|
||||
let field_expr = f.attrs.iter().find(|a| a.path.is_ident("FieldExpr"));
|
||||
if let Some(exp) = field_expr {
|
||||
let exp: syn::Expr = exp.parse_args().expect("Invalid expression");
|
||||
match_hands.extend(quote! {
|
||||
#arg => Some(EntryType::Number(#exp)),
|
||||
})
|
||||
}
|
||||
else {
|
||||
// No expression, guess we just push this and nothing special (should prob be an input but mehhh)
|
||||
match_hands.extend(quote! {
|
||||
#arg => Some(EntryType::#t(self.#name.clone())),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
match_hands.extend(quote! { _ => None, });
|
||||
let mut vec = quote! { match(entry) };
|
||||
vec.append(Group::new(Delimiter::Brace, match_hands));
|
||||
output.append(Group::new(Delimiter::Brace, vec));
|
||||
|
||||
output
|
||||
}
|
||||
|
||||
fn impl_set(data: &syn::Data) -> TokenStream {
|
||||
let mut output = TokenStream::new();
|
||||
output.extend(quote! {
|
||||
fn set(&mut self, entry: &str, value: EntryType)
|
||||
});
|
||||
|
||||
let mut match_hands = TokenStream::new();
|
||||
if let syn::Data::Struct(ds) = data {
|
||||
if let syn::Fields::Named(fs) = &ds.fields {
|
||||
for f in &fs.named {
|
||||
let name = f.ident.clone().unwrap();
|
||||
let input_attr = f.attrs.iter().find(|a| a.path.is_ident("Input"));
|
||||
if let Some(attr) = input_attr {
|
||||
let t = get_type_ident_set(&f.ty).expect(&format!("Invalid type for input: {}", name));
|
||||
let arg: syn::LitStr = attr.parse_args().expect("No arguments supplied for Input attribute, usage: `Input(\"Name\")`");
|
||||
match_hands.extend(quote! {
|
||||
#arg => self.#name = value.#t(),
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
match_hands.extend(quote! { _ => {} });
|
||||
let mut vec = quote! { match(entry) };
|
||||
vec.append(Group::new(Delimiter::Brace, match_hands));
|
||||
output.append(Group::new(Delimiter::Brace, vec));
|
||||
|
||||
output
|
||||
}
|
||||
|
||||
fn impl_display(data: &syn::Data) -> TokenStream {
|
||||
let mut output = TokenStream::new();
|
||||
output.extend(quote! {
|
||||
fn display(&self) -> Vec<Option<(String, EntryType)>>
|
||||
});
|
||||
|
||||
let mut items = TokenStream::new();
|
||||
if let syn::Data::Struct(ds) = data {
|
||||
if let syn::Fields::Named(fs) = &ds.fields {
|
||||
for f in &fs.named {
|
||||
let name = f.ident.clone().unwrap();
|
||||
if f.attrs.iter().any(|a| a.path.is_ident("Seperator")) {
|
||||
items.extend(quote! { None, })
|
||||
}
|
||||
let input_attr = f.attrs.iter().find(|a| a.path.is_ident("Input"));
|
||||
if let Some(attr) = input_attr {
|
||||
let t = get_type_ident(&f.ty).expect(&format!("Invalid type for input: {}", name));
|
||||
let arg: syn::LitStr = attr.parse_args().expect("No arguments supplied for Input attribute, usage: `Input(\"Name\")`");
|
||||
items.extend(quote! {
|
||||
Some((#arg.to_string(), EntryType::#t(self.#name.clone()))),
|
||||
|
||||
})
|
||||
}
|
||||
let field_attr = f.attrs.iter().find(|a| a.path.is_ident("Field"));
|
||||
if let Some(attr) = field_attr {
|
||||
let t = get_type_ident(&f.ty).expect(&format!("Invalid type for field: {}", name));
|
||||
let arg: syn::LitStr = attr.parse_args().expect("Invalid arguments for Field attribute");
|
||||
let field_expr = f.attrs.iter().find(|a| a.path.is_ident("FieldExpr"));
|
||||
if let Some(exp) = field_expr {
|
||||
let exp: syn::Expr = exp.parse_args().expect("Invalid expression");
|
||||
items.extend(quote! {
|
||||
Some((#arg.to_string(), EntryType::Number(#exp))),
|
||||
})
|
||||
}
|
||||
else {
|
||||
// No expression, guess we just push this and nothing special (should prob be an input but mehhh)
|
||||
items.extend(quote! {
|
||||
Some((#arg.to_string(), EntryType::#t(self.#name.clone()))),
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
let mut vec = quote! { Vec::from };
|
||||
vec.append(Group::new(Delimiter::Parenthesis, Group::new(Delimiter::Bracket, items).to_token_stream()));
|
||||
output.append(Group::new(Delimiter::Brace, vec));
|
||||
|
||||
output
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue