Skip to content

Commit c207000

Browse files
feat: add validation macro trait
1 parent 66d08a7 commit c207000

6 files changed

Lines changed: 77 additions & 35 deletions

File tree

packages/fortifier-macros/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
//! Fortifier macros.
44
55
mod validate;
6+
mod validation;
67
mod validations;
78

89
use proc_macro::TokenStream;

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

Lines changed: 23 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -2,38 +2,36 @@ use proc_macro2::TokenStream;
22
use quote::quote;
33
use syn::{Field, Result};
44

5-
use crate::validations::{Email, Length, Url};
5+
use crate::{
6+
validation::Validation,
7+
validations::{Email, Length, Url},
8+
};
69

710
pub struct ValidateField {
811
expr: TokenStream,
9-
// TODO: Consider using a trait for validations.
10-
email: Option<Email>,
11-
length: Option<Length>,
12-
url: Option<Url>,
12+
validations: Vec<Box<dyn Validation>>,
1313
}
1414

1515
impl ValidateField {
1616
pub fn parse(expr: TokenStream, field: &Field) -> Result<Self> {
1717
let mut result = Self {
1818
expr,
19-
email: None,
20-
length: None,
21-
url: None,
19+
validations: vec![],
2220
};
2321

2422
for attr in &field.attrs {
2523
if attr.path().is_ident("validate") {
2624
attr.parse_nested_meta(|meta| {
2725
if meta.path.is_ident("email") {
28-
result.email = Some(Email::parse(&meta)?);
26+
result.validations.push(Box::new(Email::parse(&meta)?));
2927

3028
Ok(())
3129
} else if meta.path.is_ident("length") {
32-
result.length = Some(Length::parse(&meta)?);
30+
result.validations.push(Box::new(Length::parse(&meta)?));
3331

3432
Ok(())
3533
} else if meta.path.is_ident("url") {
36-
result.url = Some(Url::parse(&meta)?);
34+
result.validations.push(Box::new(Url::parse(&meta)?));
3735

3836
Ok(())
3937
} else {
@@ -49,26 +47,25 @@ impl ValidateField {
4947
pub fn error_type(&self) -> TokenStream {
5048
// TODO: Merge error types
5149

52-
if self.email.is_some() {
53-
quote!(EmailError)
54-
} else if self.length.is_some() {
55-
quote!(LengthError<usize>)
56-
} else if self.url.is_some() {
57-
quote!(UrlError)
58-
} else {
59-
quote!(())
60-
}
50+
self.validations
51+
.first()
52+
.map(|validation| validation.error_type())
53+
.unwrap_or_else(|| quote!(()))
6154
}
6255

6356
pub fn sync_validations(&self) -> Vec<TokenStream> {
64-
let email = self.email.as_ref().map(|email| email.tokens(&self.expr));
65-
let length = self.length.as_ref().map(|length| length.tokens(&self.expr));
66-
let url = self.url.as_ref().map(|url| url.tokens(&self.expr));
67-
68-
[email, length, url].into_iter().flatten().collect()
57+
self.validations
58+
.iter()
59+
.filter(|validation| !validation.is_async())
60+
.map(|validation| validation.tokens(&self.expr))
61+
.collect()
6962
}
7063

7164
pub fn async_validations(&self) -> Vec<TokenStream> {
72-
vec![]
65+
self.validations
66+
.iter()
67+
.filter(|validation| validation.is_async())
68+
.map(|validation| validation.tokens(&self.expr))
69+
.collect()
7370
}
7471
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
use proc_macro2::TokenStream;
2+
use syn::{Result, meta::ParseNestedMeta};
3+
4+
pub trait Validation {
5+
fn parse(_meta: &ParseNestedMeta<'_>) -> Result<Self>
6+
where
7+
Self: Sized;
8+
9+
fn is_async(&self) -> bool;
10+
11+
fn error_type(&self) -> TokenStream;
12+
13+
fn tokens(&self, expr: &TokenStream) -> TokenStream;
14+
}

packages/fortifier-macros/src/validations/email.rs

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,25 @@ use proc_macro2::TokenStream;
22
use quote::quote;
33
use syn::{Result, meta::ParseNestedMeta};
44

5+
use crate::validation::Validation;
6+
57
#[derive(Default)]
68
pub struct Email {}
79

8-
impl Email {
9-
pub fn parse(_meta: &ParseNestedMeta<'_>) -> Result<Email> {
10+
impl Validation for Email {
11+
fn parse(_meta: &ParseNestedMeta<'_>) -> Result<Self> {
1012
Ok(Email::default())
1113
}
1214

13-
pub fn tokens(&self, expr: &TokenStream) -> TokenStream {
15+
fn is_async(&self) -> bool {
16+
false
17+
}
18+
19+
fn error_type(&self) -> TokenStream {
20+
quote!(EmailError)
21+
}
22+
23+
fn tokens(&self, expr: &TokenStream) -> TokenStream {
1424
quote! {
1525
#expr.validate_email()
1626
}

packages/fortifier-macros/src/validations/length.rs

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,17 @@ use proc_macro2::TokenStream;
22
use quote::quote;
33
use syn::{Expr, Result, meta::ParseNestedMeta};
44

5+
use crate::validation::Validation;
6+
57
#[derive(Default)]
68
pub struct Length {
79
pub equal: Option<Expr>,
810
pub min: Option<Expr>,
911
pub max: Option<Expr>,
1012
}
1113

12-
impl Length {
13-
pub fn parse(meta: &ParseNestedMeta<'_>) -> Result<Length> {
14+
impl Validation for Length {
15+
fn parse(meta: &ParseNestedMeta<'_>) -> Result<Self> {
1416
let mut result = Length::default();
1517

1618
meta.parse_nested_meta(|meta| {
@@ -37,7 +39,15 @@ impl Length {
3739
Ok(result)
3840
}
3941

40-
pub fn tokens(&self, expr: &TokenStream) -> TokenStream {
42+
fn is_async(&self) -> bool {
43+
false
44+
}
45+
46+
fn error_type(&self) -> TokenStream {
47+
quote!(LengthError<usize>)
48+
}
49+
50+
fn tokens(&self, expr: &TokenStream) -> TokenStream {
4151
let equal = if let Some(equal) = &self.equal {
4252
quote!(Some(#equal))
4353
} else {

packages/fortifier-macros/src/validations/url.rs

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,25 @@ use proc_macro2::TokenStream;
22
use quote::quote;
33
use syn::{Result, meta::ParseNestedMeta};
44

5+
use crate::validation::Validation;
6+
57
#[derive(Default)]
68
pub struct Url {}
79

8-
impl Url {
9-
pub fn parse(_meta: &ParseNestedMeta<'_>) -> Result<Url> {
10+
impl Validation for Url {
11+
fn parse(_meta: &ParseNestedMeta<'_>) -> Result<Self> {
1012
Ok(Url::default())
1113
}
1214

13-
pub fn tokens(&self, expr: &TokenStream) -> TokenStream {
15+
fn is_async(&self) -> bool {
16+
false
17+
}
18+
19+
fn error_type(&self) -> TokenStream {
20+
quote!(UrlError)
21+
}
22+
23+
fn tokens(&self, expr: &TokenStream) -> TokenStream {
1424
quote! {
1525
#expr.validate_url()
1626
}

0 commit comments

Comments
 (0)