Skip to content

Commit 7aced3d

Browse files
refactor: separate parsing and token generation (#3)
1 parent c5c3da8 commit 7aced3d

File tree

16 files changed

+406
-174
lines changed

16 files changed

+406
-174
lines changed

Cargo.toml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,3 +12,10 @@ version = "0.0.1"
1212
[workspace.dependencies]
1313
fortifier = { path = "./packages/fortifier", version = "0.0.1" }
1414
fortifier-macros = { path = "./packages/fortifier-macros", version = "0.0.1" }
15+
16+
[workspace.lints.rust]
17+
unsafe_code = "deny"
18+
19+
[workspace.lints.clippy]
20+
panic = "deny"
21+
unwrap_used = "deny"

example/Cargo.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,6 @@ version.workspace = true
1010

1111
[dependencies]
1212
fortifier.workspace = true
13+
14+
[lints]
15+
workspace = true

packages/fortifier-macros/Cargo.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,3 +20,6 @@ syn = "2.0.110"
2020
[dev-dependencies]
2121
fortifier.workspace = true
2222
trybuild = "1.0.114"
23+
24+
[lints]
25+
workspace = true
Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,23 @@
1+
#![warn(missing_docs)]
2+
3+
//! Fortifier macros.
4+
15
mod validate;
26
mod validations;
37

48
use proc_macro::TokenStream;
9+
use quote::ToTokens;
510
use syn::{DeriveInput, Error, parse_macro_input};
611

7-
use crate::validate::validate_tokens;
12+
use crate::validate::Validate;
813

14+
/// Validate derive macro.
915
#[proc_macro_derive(Validate, attributes(validate))]
1016
pub fn derive(input: TokenStream) -> TokenStream {
1117
let input = parse_macro_input!(input as DeriveInput);
1218

13-
validate_tokens(input)
19+
Validate::parse(input)
20+
.map(|validate| validate.to_token_stream())
1421
.unwrap_or_else(Error::into_compile_error)
1522
.into()
1623
}
Lines changed: 25 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,36 @@
11
mod r#enum;
2+
mod field;
23
mod r#struct;
34
mod r#union;
45

56
use proc_macro2::TokenStream;
6-
use quote::format_ident;
7+
use quote::ToTokens;
78
use syn::{Data, DeriveInput, Result};
89

9-
use crate::validate::{
10-
r#enum::validate_enum, r#struct::validate_struct_tokens, union::validate_union,
11-
};
10+
use crate::validate::{r#enum::ValidateEnum, r#struct::ValidateStruct, union::ValidateUnion};
1211

13-
pub fn validate_tokens(input: DeriveInput) -> Result<TokenStream> {
14-
let ident = input.ident;
15-
let error_ident = format_ident!("{ident}ValidationError");
12+
pub enum Validate {
13+
Struct(ValidateStruct),
14+
Enum(ValidateEnum),
15+
Union(ValidateUnion),
16+
}
17+
18+
impl Validate {
19+
pub fn parse(input: DeriveInput) -> Result<Self> {
20+
Ok(match &input.data {
21+
Data::Struct(data) => Self::Struct(ValidateStruct::parse(&input, data)?),
22+
Data::Enum(data) => Self::Enum(ValidateEnum::parse(&input, data)?),
23+
Data::Union(data) => Self::Union(ValidateUnion::parse(&input, data)?),
24+
})
25+
}
26+
}
1627

17-
match input.data {
18-
Data::Struct(data) => validate_struct_tokens(ident, error_ident, data),
19-
Data::Enum(data) => validate_enum(ident, error_ident, data),
20-
Data::Union(data) => validate_union(ident, error_ident, data),
28+
impl ToTokens for Validate {
29+
fn to_tokens(&self, tokens: &mut TokenStream) {
30+
match self {
31+
Validate::Struct(r#struct) => r#struct.to_tokens(tokens),
32+
Validate::Enum(r#enum) => r#enum.to_tokens(tokens),
33+
Validate::Union(r#union) => r#union.to_tokens(tokens),
34+
}
2135
}
2236
}
Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,17 @@
11
use proc_macro2::TokenStream;
2-
use syn::{DataEnum, Ident, Result};
2+
use quote::ToTokens;
3+
use syn::{DataEnum, DeriveInput, Result};
34

4-
pub fn validate_enum(_ident: Ident, _error_ident: Ident, _data: DataEnum) -> Result<TokenStream> {
5-
todo!("enum")
5+
pub struct ValidateEnum {}
6+
7+
impl ValidateEnum {
8+
pub fn parse(_input: &DeriveInput, _data: &DataEnum) -> Result<Self> {
9+
todo!("enum")
10+
}
11+
}
12+
13+
impl ToTokens for ValidateEnum {
14+
fn to_tokens(&self, _tokens: &mut TokenStream) {
15+
// TODO
16+
}
617
}
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
use proc_macro2::TokenStream;
2+
use quote::quote;
3+
use syn::{Field, Ident, Result};
4+
5+
use crate::validations::{Email, Length};
6+
7+
pub struct ValidateField {
8+
ident: Ident,
9+
// TODO: Consider using a trait for validations.
10+
email: Option<Email>,
11+
length: Option<Length>,
12+
}
13+
14+
impl ValidateField {
15+
pub fn parse(ident: Ident, field: &Field) -> Result<Self> {
16+
let mut result = Self {
17+
ident,
18+
email: None,
19+
length: None,
20+
};
21+
22+
for attr in &field.attrs {
23+
if attr.path().is_ident("validate") {
24+
attr.parse_nested_meta(|meta| {
25+
if meta.path.is_ident("email") {
26+
result.email = Some(Email::parse(&meta)?);
27+
28+
Ok(())
29+
} else if meta.path.is_ident("length") {
30+
result.length = Some(Length::parse(&meta)?);
31+
32+
Ok(())
33+
} else {
34+
Err(meta.error("unknown validate parameter"))
35+
}
36+
})?;
37+
}
38+
}
39+
40+
Ok(result)
41+
}
42+
43+
pub fn error_type(&self) -> TokenStream {
44+
// TODO: Merge error types
45+
46+
if self.email.is_some() {
47+
quote!(EmailError)
48+
} else if self.length.is_some() {
49+
quote!(LengthError<usize>)
50+
} else {
51+
quote!(())
52+
}
53+
}
54+
55+
pub fn sync_validations(&self) -> Vec<TokenStream> {
56+
let email = self.email.as_ref().map(|email| email.tokens(&self.ident));
57+
let length = self
58+
.length
59+
.as_ref()
60+
.map(|length| length.tokens(&self.ident));
61+
62+
[email, length].into_iter().flatten().collect()
63+
}
64+
65+
pub fn async_validations(&self) -> Vec<TokenStream> {
66+
vec![]
67+
}
68+
}

0 commit comments

Comments
 (0)