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