square_ox_derive/
lib.rs

1use proc_macro::{TokenStream};
2use quote::quote;
3use syn::{parse_macro_input, DeriveInput, Data, Fields, Ident, Type, Field, LitStr, DataStruct, PathArguments, GenericArgument};
4use syn::__private::{str, TokenStream2};
5use syn::parse::ParseStream;
6
7#[derive(Clone)]
8struct CustomField {
9    name: Ident,
10    ty: Type,
11    option: bool,
12    attributes: Vec<Attribute>
13}
14#[proc_macro_derive(Builder, attributes(builder_vis, builder_rand, builder_validate, builder_into))]
15pub fn builder(
16    annotated_item: TokenStream,
17)
18    -> TokenStream
19{
20    let input = parse_macro_input!(annotated_item as DeriveInput);
21    let struct_name = input.ident;
22    let fields = if let syn::Data::Struct(syn::DataStruct {
23        fields: syn::Fields::Named(syn::FieldsNamed {ref named, ..}),
24        ..
25    }) = input.data {
26        named
27    } else {
28        panic!("struct {} has no fields", struct_name);
29    };
30
31    let mut field_parser = FieldParser::new();
32
33    field_parser.fields = fields.iter().map(
34        | Field { ident, ty, attrs, ..} |
35            {
36                let (ty, option) = FieldParser::unwrap_type(ty);
37                let attrs = attrs.iter().filter_map(|attr| {
38                    if let Some(ident) = attr.path.get_ident() {
39                        match ident.to_string().as_str() {
40                            "builder_vis" => {
41                                Some(match attr.parse_args::<LitStr>() {
42                                    Ok(str) => match str.value().as_str() {
43                                        "private" => Attribute::Vis(Vis::Private),
44                                        "public" => Attribute::Vis(Vis::Public),
45                                        _ => panic!("the args passed with builder_vis are invalid")
46                                    }
47                                    _ => panic!("must pass string as arg with builder_vis")
48                                })
49                            },
50                            "builder_rand" => {
51                                Some(match attr.parse_args::<LitStr>() {
52                                    Ok(str) => match str.value().as_str() {
53                                        "uuid" => Attribute::ValidatiobStep(ValidationStep::Set(SetType::Uuid)),
54                                        _ => panic!("the args passed with builder_rand are invalid")
55                                    }
56                                    _ => panic!("must pass string as arg with builder_rand")
57                                })
58                            },
59                            "builder_validate" => {
60                                Some(match attr.parse_args::<LitStr>() {
61                                    Ok(str) => match str.value().as_str() {
62                                        "is_some" => Attribute::ValidatiobStep(ValidationStep::Check(CheckType::Some)),
63                                        "len" => Attribute::ValidatiobStep(ValidationStep::Check(CheckType::Len(0))),
64                                        _ => panic!("the args passed with builder_validate are invalid")
65                                    }
66                                    _ => panic!("must pass string as arg with builder_validate")
67                                })
68                            },
69                            "builder_into" => {
70                                Some(Attribute::Into)
71                            }
72                            _ => None
73                        }
74                    } else {
75                        None
76                    }
77                }).collect::<Vec<Attribute>>();
78
79                CustomField {
80                    name: ident.clone().unwrap(),
81                    ty: ty,
82                    option,
83                    attributes: attrs
84                }
85            }
86    ).collect();
87
88    let FieldPaserserMapResult {
89        validation_checks,
90        validation_sets,
91        implemented_methods,
92    } = field_parser.map_fields();
93
94    let stream = quote! {
95        impl crate::builder::Validate for #struct_name
96        {
97            fn validate(mut self)
98            -> Result<Self, crate::errors::ValidationError> where Self: Sized
99            {
100                if true #(#validation_checks)* {
101                    #(#validation_sets)*
102                    Ok(self)
103                } else {
104                    Err(crate::errors::ValidationError)
105                }
106            }
107        }
108
109        impl<T: crate::builder::ParentBuilder> crate::builder::Builder<#struct_name, T>
110        {
111            #(#implemented_methods)*
112        }
113
114    }.into();
115
116    return stream
117}
118
119#[derive(Default)]
120struct FieldParser {
121    fields: Vec<CustomField>,
122}
123
124impl FieldParser {
125    fn new() -> Self {
126        std::default::Default::default()
127    }
128
129    fn unwrap_type<'a>(ty: &'a Type) -> (Type, bool) {
130        if let syn::Type::Path(syn::TypePath { path, .. }) = ty {
131            if let Some(segment) = path.segments.last() {
132                match segment.ident.to_string().as_str() {
133                    "Option" => {
134                        match &segment.arguments {
135                            PathArguments::AngleBracketed(args) => match args.args.first() {
136                                Some(GenericArgument::Type(ty)) => return (ty.clone(), true),
137                                _ => panic!("option argument not a type"),
138                            }
139                            _ => panic!("argument to option not an AngleBracketed argument"),
140                        }
141                    }
142                    _ => return (ty.clone(), false)
143                }
144            } else {
145                panic!("field does not have a type")
146            }
147        } else {
148            panic!("field does not have a type")
149        };
150    }
151
152    fn map_fields(&self) -> FieldPaserserMapResult {
153        let mut res = FieldPaserserMapResult {
154            validation_checks: vec![],
155            validation_sets: vec![],
156            implemented_methods: vec![],
157        };
158
159        self.fields
160            .iter()
161            .for_each(|
162                cf
163            |{
164                if !cf.attributes.iter().fold(false, |acc, attribute| {
165                    if let Attribute::Vis(Vis::Private) = attribute {
166                        true
167                    } else {
168                        acc
169                    }
170                }) {
171                    res.implemented_methods.push(Self::implement_method(cf.clone()))
172                }
173            });
174
175        self.fields
176            .iter()
177            .for_each(|
178                cf
179            |{
180            cf.attributes.iter().for_each(|attr| match attr {
181                Attribute::ValidatiobStep(ValidationStep::Check(_)) => {
182                    res.validation_checks.push(Self::implement_validation_step(cf.name.clone(), attr.clone()));
183                },
184                Attribute::ValidatiobStep(ValidationStep::Set(_)) => {
185                    res.validation_sets.push(Self::implement_validation_step(cf.name.clone(), attr.clone()));
186                },
187                _ => (),
188            })
189        });
190
191        res
192    }
193
194    fn implement_method(field: CustomField) -> TokenStream2 {
195        let CustomField { name, ty, option, attributes } = field;
196
197        let into = attributes.iter().fold(false, |acc, attr| {
198            Attribute::Into == *attr || acc
199        });
200
201        let variable = match into {
202            true => quote!{ #name.into() },
203            false => quote!{ #name },
204        };
205
206        let body = match option {
207            true => quote!{
208                self.body.#name = Some(#variable);
209
210                self
211            },
212            false => quote!{
213                self.body.#name = #variable;
214
215                self
216            },
217        };
218
219        return match into {
220            true => quote!{
221                pub fn #name<I: Into<#ty>>(mut self, #name: I) -> Self {
222                    #body
223                }
224            },
225            false => quote!{
226                pub fn #name(mut self, #name: #ty) -> Self {
227                    #body
228                }
229            },
230        };
231    }
232
233    fn implement_validation_step(name: Ident, attr: Attribute) -> TokenStream2 {
234
235        match attr {
236            Attribute::ValidatiobStep(ValidationStep::Check(CheckType::Some)) =>
237                    quote! { && self.#name.is_some() },
238            Attribute::ValidatiobStep(ValidationStep::Check(CheckType::Len(_))) =>
239                    quote! { && self.#name.len() > 0 },
240            Attribute::ValidatiobStep(ValidationStep::Set(SetType::Uuid)) =>
241                    quote! { self.#name = Some(uuid::Uuid::new_v4().to_string()); },
242            Attribute::Vis(_) => panic!("cannot pass vis attribute to implement_validation_step"),
243            Attribute::Into => panic!("cannot pass vis attribute to implement_validation_step"),
244        }
245    }
246}
247
248#[derive(Clone)]
249struct FieldPaserserMapResult {
250    validation_checks: Vec<TokenStream2>,
251    validation_sets: Vec<TokenStream2>,
252    implemented_methods: Vec<TokenStream2>,
253}
254
255#[derive(Clone, PartialEq)]
256enum Attribute {
257    ValidatiobStep(ValidationStep),
258    Vis(Vis),
259    Into
260}
261
262#[derive(Clone, PartialEq)]
263enum Vis {
264    Private,
265    Public
266}
267
268#[derive(Clone, PartialEq)]
269enum ValidationStep {
270    Check(CheckType),
271    Set(SetType),
272}
273
274#[derive(Clone, PartialEq)]
275enum SetType {
276    Uuid,
277}
278
279#[derive(Clone, PartialEq)]
280enum CheckType {
281    Some,
282    Len(i32),
283}
284
285
286