Skip to content

Commit fb0a053

Browse files
refactor: reduce code duplication (#16)
1 parent 59c3027 commit fb0a053

12 files changed

Lines changed: 195 additions & 341 deletions

File tree

packages/fortifier-macros/src/validate/enum.rs

Lines changed: 21 additions & 91 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
1-
use std::{collections::HashSet, str::FromStr};
2-
31
use proc_macro2::TokenStream;
42
use quote::{ToTokens, TokenStreamExt, format_ident, quote};
53
use syn::{DataEnum, DeriveInput, Generics, Ident, Result, Variant, Visibility};
64

7-
use crate::validate::{
8-
field::{LiteralOrIdent, ValidateFieldPrefix},
9-
fields::ValidateFields,
5+
use crate::{
6+
validate::{
7+
field::{LiteralOrIdent, ValidateFieldPrefix},
8+
fields::ValidateFields,
9+
},
10+
validation::Execution,
1011
};
1112

1213
pub struct ValidateEnum {
@@ -39,13 +40,6 @@ impl ValidateEnum {
3940
Ok(result)
4041
}
4142

42-
fn uses(&self) -> HashSet<String> {
43-
self.variants
44-
.iter()
45-
.flat_map(|variant| variant.uses())
46-
.collect()
47-
}
48-
4943
fn error_type(&self) -> (Ident, TokenStream) {
5044
let visibility = &self.visibility;
5145
let error_ident = &self.error_ident;
@@ -89,42 +83,33 @@ impl ToTokens for ValidateEnum {
8983
let ident = &self.ident;
9084
let (impl_generics, type_generics, where_clause) = &self.generics.split_for_impl();
9185

92-
let uses = self.uses().into_iter().map(|r#use| {
93-
let tokens = TokenStream::from_str(&r#use).expect("Tokens should be valid.");
94-
quote!(use #tokens;)
95-
});
9686
let (error_ident, error_type) = self.error_type();
9787
let variant_error_types = self.variants.iter().map(|variant| variant.error_type().1);
9888
let sync_variant_match_arms = self
9989
.variants
10090
.iter()
101-
.map(|variant| variant.sync_match_arm_tokens());
91+
.map(|variant| variant.match_arm(Execution::Sync));
10292
let async_variant_match_arms = self
10393
.variants
10494
.iter()
105-
.map(|variant| variant.async_match_arm_tokens());
95+
.map(|variant| variant.match_arm(Execution::Async));
10696

10797
tokens.append_all(quote! {
108-
#( #uses )*
109-
110-
// TODO: Replace with granular uses.
111-
use fortifier::*;
112-
11398
#error_type
11499

115100
#( #variant_error_types )*
116101

117102
#[automatically_derived]
118-
impl #impl_generics Validate for #ident #type_generics #where_clause {
103+
impl #impl_generics ::fortifier::Validate for #ident #type_generics #where_clause {
119104
type Error = #error_ident;
120105

121-
fn validate_sync(&self) -> Result<(), ValidationErrors<Self::Error>> {
106+
fn validate_sync(&self) -> Result<(), ::fortifier::ValidationErrors<Self::Error>> {
122107
match &self {
123108
#( #sync_variant_match_arms ),*
124109
}
125110
}
126111

127-
fn validate_async(&self) -> ::std::pin::Pin<Box<impl Future<Output = Result<(), ValidationErrors<Self::Error>>>>> {
112+
fn validate_async(&self) -> ::std::pin::Pin<Box<impl Future<Output = Result<(), ::fortifier::ValidationErrors<Self::Error>>>>> {
128113
Box::pin(async move {
129114
match &self {
130115
#( #async_variant_match_arms ),*
@@ -164,66 +149,11 @@ impl ValidateEnumVariant {
164149
Ok(result)
165150
}
166151

167-
fn uses(&self) -> HashSet<String> {
168-
self.fields.uses()
169-
}
170-
171-
fn error_type(&self) -> (Ident, TokenStream) {
152+
fn error_type(&self) -> (TokenStream, TokenStream) {
172153
self.fields.error_type()
173154
}
174155

175-
fn sync_match_arm_tokens(&self) -> TokenStream {
176-
let enum_ident = &self.enum_ident;
177-
let enum_error_ident = &self.enum_error_ident;
178-
let ident = &self.ident;
179-
180-
let error_wrapper = |tokens| quote!(#enum_error_ident::#ident(#tokens));
181-
182-
match &self.fields {
183-
ValidateFields::Named(fields) => {
184-
let field_idents = fields.idents();
185-
let sync_validations =
186-
fields.sync_validations(ValidateFieldPrefix::None, &error_wrapper);
187-
188-
// TODO: Only destructure fields required for validation.
189-
quote! {
190-
#[allow(unused_variables)]
191-
#enum_ident::#ident {
192-
#( #field_idents ),*
193-
} => {
194-
#sync_validations
195-
}
196-
}
197-
}
198-
ValidateFields::Unnamed(fields) => {
199-
let field_idents = fields.idents().map(|ident| match ident {
200-
LiteralOrIdent::Literal(literal) => format_ident!("f{literal}"),
201-
LiteralOrIdent::Ident(ident) => ident.clone(),
202-
});
203-
let sync_validations =
204-
fields.sync_validations(ValidateFieldPrefix::F, &error_wrapper);
205-
206-
quote! {
207-
#enum_ident::#ident(
208-
#( #field_idents ),*
209-
) => {
210-
#sync_validations
211-
}
212-
}
213-
}
214-
ValidateFields::Unit(fields) => {
215-
let sync_validations = fields.sync_validations();
216-
217-
quote! {
218-
#enum_ident::#ident => {
219-
#sync_validations
220-
}
221-
}
222-
}
223-
}
224-
}
225-
226-
fn async_match_arm_tokens(&self) -> TokenStream {
156+
fn match_arm(&self, exeuction: Execution) -> TokenStream {
227157
let enum_ident = &self.enum_ident;
228158
let enum_error_ident = &self.enum_error_ident;
229159
let ident = &self.ident;
@@ -233,16 +163,16 @@ impl ValidateEnumVariant {
233163
match &self.fields {
234164
ValidateFields::Named(fields) => {
235165
let field_idents = fields.idents();
236-
let async_validations =
237-
fields.async_validations(ValidateFieldPrefix::None, &error_wrapper);
166+
let validations =
167+
fields.validations(exeuction, ValidateFieldPrefix::None, &error_wrapper);
238168

239169
// TODO: Only destructure fields required for validation.
240170
quote! {
241171
#[allow(unused_variables)]
242172
#enum_ident::#ident {
243173
#( #field_idents ),*
244174
} => {
245-
#async_validations
175+
#validations
246176
}
247177
}
248178
}
@@ -251,23 +181,23 @@ impl ValidateEnumVariant {
251181
LiteralOrIdent::Literal(literal) => format_ident!("f{literal}"),
252182
LiteralOrIdent::Ident(ident) => ident.clone(),
253183
});
254-
let async_validations =
255-
fields.async_validations(ValidateFieldPrefix::F, &error_wrapper);
184+
let validations =
185+
fields.validations(exeuction, ValidateFieldPrefix::F, &error_wrapper);
256186

257187
quote! {
258188
#enum_ident::#ident(
259189
#( #field_idents ),*
260190
) => {
261-
#async_validations
191+
#validations
262192
}
263193
}
264194
}
265195
ValidateFields::Unit(fields) => {
266-
let async_validations = fields.async_validations();
196+
let validations = fields.validations();
267197

268198
quote! {
269199
#enum_ident::#ident => {
270-
#async_validations
200+
#validations
271201
}
272202
}
273203
}

packages/fortifier-macros/src/validate/field.rs

Lines changed: 24 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use quote::{ToTokens, format_ident, quote};
44
use syn::{Field, Ident, Result, Visibility};
55

66
use crate::{
7-
validation::Validation,
7+
validation::{Execution, Validation},
88
validations::{Custom, Email, Length, Regex, Url},
99
};
1010

@@ -127,64 +127,38 @@ impl ValidateField {
127127
}
128128
}
129129

130-
pub fn sync_validations(&self, field_prefix: ValidateFieldPrefix) -> Vec<TokenStream> {
130+
pub fn validations(
131+
&self,
132+
execution: Execution,
133+
field_prefix: ValidateFieldPrefix,
134+
) -> Vec<TokenStream> {
131135
let error_type_ident = &self.error_type_ident;
132136
let ident = &self.ident;
133137

134-
self.validations
135-
.iter()
136-
.filter(|validation| !validation.is_async())
137-
.map(|validation| {
138-
let validation_ident = validation.ident();
139-
let tokens = validation.tokens(&match field_prefix {
140-
ValidateFieldPrefix::None => self.ident.to_token_stream(),
141-
ValidateFieldPrefix::SelfKeyword => quote!(self.#ident),
142-
ValidateFieldPrefix::F => match &self.ident {
143-
LiteralOrIdent::Literal(literal) => {
144-
format_ident!("f{literal}").to_token_stream()
145-
}
146-
LiteralOrIdent::Ident(ident) => ident.to_token_stream(),
147-
},
148-
});
149-
150-
if self.validations.len() > 1 {
151-
quote! {
152-
#tokens.map_err(#error_type_ident::#validation_ident)
153-
}
154-
} else {
155-
tokens
156-
}
157-
})
158-
.collect()
159-
}
160-
161-
pub fn async_validations(&self, field_prefix: ValidateFieldPrefix) -> Vec<TokenStream> {
162-
let ident = &self.ident;
163-
let error_type_ident = &self.error_type_ident;
138+
let field_expr = match field_prefix {
139+
ValidateFieldPrefix::None => self.ident.to_token_stream(),
140+
ValidateFieldPrefix::SelfKeyword => quote!(self.#ident),
141+
ValidateFieldPrefix::F => match &self.ident {
142+
LiteralOrIdent::Literal(literal) => format_ident!("f{literal}").to_token_stream(),
143+
LiteralOrIdent::Ident(ident) => ident.to_token_stream(),
144+
},
145+
};
164146

165147
self.validations
166148
.iter()
167-
.filter(|validation| validation.is_async())
168-
.map(|validation| {
149+
.flat_map(|validation| {
169150
let validation_ident = validation.ident();
170-
let tokens = validation.tokens(&match field_prefix {
171-
ValidateFieldPrefix::None => self.ident.to_token_stream(),
172-
ValidateFieldPrefix::SelfKeyword => quote!(self.#ident),
173-
ValidateFieldPrefix::F => match &self.ident {
174-
LiteralOrIdent::Literal(literal) => {
175-
format_ident!("f{literal}").to_token_stream()
176-
}
177-
LiteralOrIdent::Ident(ident) => ident.to_token_stream(),
178-
},
179-
});
151+
let expr = validation.expr(execution, &field_expr);
180152

181-
if self.validations.len() > 1 {
182-
quote! {
183-
#tokens.map_err(#error_type_ident::#validation_ident)
153+
expr.map(|expr| {
154+
if self.validations.len() > 1 {
155+
quote! {
156+
#expr.map_err(#error_type_ident::#validation_ident)
157+
}
158+
} else {
159+
expr
184160
}
185-
} else {
186-
tokens
187-
}
161+
})
188162
})
189163
.collect()
190164
}

0 commit comments

Comments
 (0)