Skip to main content

grafix_toolbox_macros/
derives.rs

1use syn::{Attribute, Item, Path, Token, parse, parse_quote, punctuated::Punctuated};
2use {proc_macro::TokenStream, quote::quote};
3type A = Vec<Attribute>;
4
5#[proc_macro_attribute]
6pub fn derive_as_obj(attrs: TokenStream, tgt_struct: TokenStream) -> TokenStream {
7	match_tgt(attrs, tgt_struct, add_object)
8}
9#[proc_macro_attribute]
10pub fn derive_as_val(attrs: TokenStream, tgt_struct: TokenStream) -> TokenStream {
11	match_tgt(attrs, tgt_struct, add_value)
12}
13#[proc_macro_attribute]
14pub fn derive_as_trivial(attrs: TokenStream, tgt_struct: TokenStream) -> TokenStream {
15	match_tgt(attrs, tgt_struct, add_trivial)
16}
17#[proc_macro_attribute]
18pub fn derive_as_ser(attrs: TokenStream, tgt_struct: TokenStream) -> TokenStream {
19	match_tgt(attrs, tgt_struct, pass)
20}
21
22fn match_tgt(a: TokenStream, tgt: TokenStream, add: fn(&mut A) -> &mut A) -> TokenStream {
23	let extra_derives = syn::parse_macro_input!(a as DerivesArgs);
24	match syn::parse_macro_input!(tgt as Item) {
25		Item::Struct(mut i) => {
26			add_serde(add(add_extra(&mut i.attrs, extra_derives.derives)));
27			quote!(#i)
28		}
29		Item::Enum(mut i) => {
30			add_serde(add(add_extra(&mut i.attrs, extra_derives.derives)));
31			quote!(#i)
32		}
33		o => syn::Error::new_spanned(o, "The #[derives_as_*] attribute can only be used on structs or enums").to_compile_error(),
34	}
35	.into()
36}
37
38fn add_extra(attrs: &mut A, derives: Punctuated<Path, Token![,]>) -> &mut A {
39	if derives.is_empty() {
40		return attrs;
41	}
42
43	attrs.push(parse_quote!(#[derive(#derives)]));
44	attrs
45}
46fn pass(attrs: &mut A) -> &mut A {
47	attrs
48}
49fn add_object(attrs: &mut A) -> &mut A {
50	attrs.push(parse_quote!(#[derive(Debug, Clone)]));
51	attrs
52}
53fn add_value(attrs: &mut A) -> &mut A {
54	attrs.push(parse_quote!(#[derive(Copy, PartialEq, PartialOrd)]));
55	add_object(attrs)
56}
57fn add_trivial(attrs: &mut A) -> &mut A {
58	attrs.push(parse_quote!(#[derive(Default)]));
59	add_value(attrs)
60}
61fn add_serde(attrs: &mut A) -> &mut A {
62	#[cfg(feature = "serde")]
63	{
64		attrs.push(parse_quote!(#[derive(serde::Serialize, serde::Deserialize)]));
65		attrs.push(parse_quote!(#[serde(crate = "serde")]));
66	}
67	attrs
68}
69
70struct DerivesArgs {
71	derives: Punctuated<Path, Token![,]>,
72}
73impl parse::Parse for DerivesArgs {
74	fn parse(input: parse::ParseStream) -> syn::Result<Self> {
75		let derives = Punctuated::<Path, Token![,]>::parse_terminated(input)?;
76		Ok(DerivesArgs { derives })
77	}
78}