nidrs_valid_macro/
lib.rs

1use proc_macro::TokenStream;
2use proc_macro2::TokenStream as TokenStream2;
3use quote::{quote, ToTokens};
4use syn::{parse::Parse, parse_macro_input, punctuated::Punctuated, Data, DeriveInput, Expr, Meta};
5
6#[proc_macro_attribute]
7pub fn dto(args: TokenStream, input: TokenStream) -> TokenStream {
8    let raw_input = TokenStream2::from(input.clone());
9
10    TokenStream::from(quote! {
11        #[derive(serde::Serialize, serde::Deserialize, Debug, nidrs::valid::Validator)]
12        #raw_input
13    })
14}
15
16#[proc_macro_derive(Validator, attributes(rule))]
17pub fn validate_derive(input: TokenStream) -> TokenStream {
18    let input = parse_macro_input!(input as DeriveInput);
19
20    let name = &input.ident;
21    let mut validations: Vec<TokenStream2> = vec![];
22    // println!("data: {:?}", input.data);
23
24    if let Data::Struct(data) = input.data {
25        for field in data.fields {
26            let field_name = &field.ident;
27
28            for attr in field.attrs.iter().filter(|a| a.path().is_ident("rule")) {
29                let meta = attr.meta.clone();
30
31                if let Meta::List(meta_list) = meta {
32                    let sub = meta_list.path.to_token_stream().to_string();
33                    if sub == "rule" {
34                        let args: TokenArgs = meta_list.parse_args().unwrap();
35                        let rule = args.args.first().expect("rule is required");
36                        let message = args
37                            .args
38                            .get(1)
39                            .map(|msg| {
40                                quote! {
41                                    Some(#msg.to_string())
42                                }
43                            })
44                            .unwrap_or(quote! {
45                                None
46                            });
47                        validations.push(quote! {
48                            let v = &self.#field_name;
49                            #rule.valid(v, stringify!(#field_name), #message)?;
50                        });
51                    }
52                }
53            }
54        }
55    } else if let Data::Enum(data) = input.data {
56        let mut variants = vec![];
57        for variant in data.variants {
58            let variant_name = &variant.ident;
59            variants.push(quote! {
60                #name::#variant_name(v) => v.valid()?,
61            });
62        }
63        validations.push(quote! {
64            match self {
65                #(#variants)*
66            }
67        });
68    } else {
69        panic!("not supported")
70    }
71    let expanded = quote! {
72        impl nidrs::valid::validator::Validator for #name{
73            fn valid(&self) -> nidrs::valid::validator::ValidResult {
74                use nidrs::valid::validator::Rule;
75                use nidrs::valid::ruleset;
76                use nidrs::valid::ruleset::*;
77                #(#validations)*
78                return Ok(());
79            }
80
81            fn example(&self) -> Vec<serde_json::Value> {
82                vec![]
83            }
84        }
85    };
86
87    TokenStream::from(expanded)
88}
89
90#[derive(Clone, Debug)]
91struct TokenArgs {
92    pub args: Vec<Expr>,
93}
94
95impl Parse for TokenArgs {
96    fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
97        let items: Punctuated<Expr, syn::Token![,]> = Punctuated::parse_terminated(input)?;
98        let mut args = Vec::new();
99        items.iter().for_each(|item| {
100            args.push(item.clone());
101        });
102        Ok(TokenArgs { args })
103    }
104}