Skip to main content

saa_proto_core/
lib.rs

1mod utils;
2use quote::{ToTokens, quote};
3use proc_macro::TokenStream;
4use syn::{parse_macro_input, parse_quote, AttributeArgs, DeriveInput, Meta, MetaList, NestedMeta};
5use utils::{fallible_macro, Options};
6
7
8fn strum_enum(input: &DeriveInput, attr_args: &[NestedMeta]) -> proc_macro2::TokenStream {
9    // Extract optional name(...) argument
10    let name_arg = attr_args.iter().find_map(|meta| {
11        if let NestedMeta::Meta(Meta::List(MetaList { path, nested, .. })) = meta {
12            if path.is_ident("name") {
13                return Some(quote! { name(#nested) });
14            }
15        }
16        None
17    });
18
19    let maybe_name = if let Some(name) = name_arg {
20        quote! { #name, }
21    } else {
22        quote! {}
23    };
24    
25    let ident = &input.ident;
26    quote! {
27        #[derive(
28            Clone, Debug, PartialEq,
29            ::saa_schema::strum_macros::Display,
30            ::saa_schema::strum_macros::EnumDiscriminants,
31            ::saa_schema::strum_macros::VariantNames
32        )]
33        #[strum_discriminants(
34            #maybe_name
35            derive(
36                ::saa_schema::strum_macros::Display,
37                ::saa_schema::strum_macros::EnumString,
38                ::saa_schema::strum_macros::VariantArray,
39                ::saa_schema::strum_macros::AsRefStr
40            ),
41            strum(serialize_all = "snake_case", crate = "::saa_schema::strum")
42        )]
43        #[strum(serialize_all = "snake_case", crate = "::saa_schema::strum")]
44        #[allow(clippy::derive_partial_eq_without_eq)]
45        #input
46
47        impl ::saa_schema::strum::IntoDiscriminant for Box<#ident> {
48            type Discriminant = <#ident as ::saa_schema::strum::IntoDiscriminant>::Discriminant;
49            fn discriminant(&self) -> Self::Discriminant {
50                (*self).discriminant()
51            }
52        }
53    }
54}
55
56
57
58
59#[proc_macro_attribute]
60pub fn saa_type(
61    _attr: TokenStream,
62    input: TokenStream,
63) -> TokenStream {
64    let input_ast = parse_macro_input!(input as DeriveInput);
65    match &input_ast.data {
66        syn::Data::Struct(_) => {
67            quote! {
68                #[derive(Clone, Debug, PartialEq)]
69                #[allow(clippy::derive_partial_eq_without_eq)]
70                #input_ast
71            }.into()
72        },
73        syn::Data::Enum(_) => {
74            quote! {
75                #[derive(Clone, Debug, PartialEq)]
76                #[allow(clippy::derive_partial_eq_without_eq)]
77                #input_ast
78            }.into()
79        },
80        syn::Data::Union(_) => panic!("unions are not supported"),
81    }
82}
83
84
85
86fallible_macro! {
87    #[proc_macro_attribute]
88    pub fn saa_error(
89        attr: proc_macro::TokenStream,
90        input: proc_macro::TokenStream,
91    ) -> syn::Result<proc_macro::TokenStream> {
92        let options = syn::parse(attr)?;
93        let input = syn::parse(input)?;
94        let expanded = saa_error_impl(input, options)?;
95        Ok(expanded.into_token_stream().into())
96    }
97}
98
99
100
101fn saa_error_impl(input: DeriveInput, options: Options) -> syn::Result<DeriveInput> {
102    let crate_path = &options.crate_path;
103    let error_path: syn::Path = syn::parse_quote!(#crate_path::thiserror::Error);
104    let mut stream = quote! {
105        #[derive(Debug, #error_path)]
106    };
107    match &input.data {
108        syn::Data::Enum(_) => {},
109        _ => return Err(syn::Error::new_spanned(&input, "Only enums are supported")),
110    };
111    stream.extend(input.to_token_stream());
112    syn::parse2(stream)
113}
114
115
116
117#[proc_macro_attribute]
118pub fn saa_derivable(
119    attr: TokenStream,
120    input: TokenStream,
121) -> TokenStream {
122    let attr_args = parse_macro_input!(attr as AttributeArgs);
123    let input_ast = parse_macro_input!(input as DeriveInput);
124    match &input_ast.data {
125        syn::Data::Struct(_) => {
126            quote! {
127                #[derive(Clone, Debug, PartialEq)]
128                #[allow(clippy::derive_partial_eq_without_eq)]
129                #input_ast
130            }.into()
131        },
132        syn::Data::Enum(_) => {
133            strum_enum(&input_ast, &attr_args).into()
134        },
135        syn::Data::Union(_) => panic!("unions are not supported"),
136    }
137}
138
139
140
141
142#[proc_macro_attribute]
143pub fn saa_str_struct(
144    _attr: proc_macro::TokenStream,
145    input: proc_macro::TokenStream,
146) -> proc_macro::TokenStream {
147    let input = parse_macro_input!(input as DeriveInput);
148
149    let expanded : DeriveInput = match input.data {
150        syn::Data::Struct(_) => parse_quote! {
151            #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Default)]
152            #input 
153        },
154        syn::Data::Enum(_) => panic!("enums are not supported"),
155        syn::Data::Union(_) => panic!("unions are not supported"),
156    };
157
158    let stream = expanded.into_token_stream();
159
160    proc_macro::TokenStream::from(stream)
161}