I DID STUFF, GOD A PROC MACRO AND SHIT

This commit is contained in:
Rusty Striker 2024-09-22 23:46:51 +03:00
commit 22319e84a1
Signed by: RustyStriker
GPG key ID: 87E4D691632DFF15
28 changed files with 3101 additions and 0 deletions

101
tavern_macros/Cargo.lock generated Normal file
View file

@ -0,0 +1,101 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "darling"
version = "0.13.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a01d95850c592940db9b8194bc39f4bc0e89dee5c4265e4b1807c34a9aba453c"
dependencies = [
"darling_core",
"darling_macro",
]
[[package]]
name = "darling_core"
version = "0.13.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "859d65a907b6852c9361e3185c862aae7fafd2887876799fa55f5f99dc40d610"
dependencies = [
"fnv",
"ident_case",
"proc-macro2",
"quote",
"strsim",
"syn",
]
[[package]]
name = "darling_macro"
version = "0.13.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c972679f83bdf9c42bd905396b6c3588a843a17f0f16dfcfa3e2c5d57441835"
dependencies = [
"darling_core",
"quote",
"syn",
]
[[package]]
name = "fnv"
version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
[[package]]
name = "ident_case"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
[[package]]
name = "proc-macro2"
version = "1.0.86"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.37"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af"
dependencies = [
"proc-macro2",
]
[[package]]
name = "strsim"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
[[package]]
name = "syn"
version = "1.0.109"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "tavern_macros"
version = "0.1.0"
dependencies = [
"darling",
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "unicode-ident"
version = "1.0.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe"

13
tavern_macros/Cargo.toml Normal file
View file

@ -0,0 +1,13 @@
[package]
name = "tavern_macros"
version = "0.1.0"
edition = "2021"
[lib]
proc-macro = true
[dependencies]
darling = "0.13"
proc-macro2 = "1.0"
quote = "1.0"
syn = { version = "1.0", features = ["full"] }

258
tavern_macros/src/lib.rs Normal file
View 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
}