Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,10 @@ version = "0.0.1"
[workspace.dependencies]
fortifier = { path = "./packages/fortifier", version = "0.0.1" }
fortifier-macros = { path = "./packages/fortifier-macros", version = "0.0.1" }

[workspace.lints.rust]
unsafe_code = "deny"

[workspace.lints.clippy]
panic = "deny"
unwrap_used = "deny"
3 changes: 3 additions & 0 deletions example/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,6 @@ version.workspace = true

[dependencies]
fortifier.workspace = true

[lints]
workspace = true
3 changes: 3 additions & 0 deletions packages/fortifier-macros/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,6 @@ syn = "2.0.110"
[dev-dependencies]
fortifier.workspace = true
trybuild = "1.0.114"

[lints]
workspace = true
11 changes: 9 additions & 2 deletions packages/fortifier-macros/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,16 +1,23 @@
#![warn(missing_docs)]

//! Fortifier macros.

mod validate;
mod validations;

use proc_macro::TokenStream;
use quote::ToTokens;
use syn::{DeriveInput, Error, parse_macro_input};

use crate::validate::validate_tokens;
use crate::validate::Validate;

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

validate_tokens(input)
Validate::parse(input)
.map(|validate| validate.to_token_stream())
.unwrap_or_else(Error::into_compile_error)
.into()
}
36 changes: 25 additions & 11 deletions packages/fortifier-macros/src/validate.rs
Original file line number Diff line number Diff line change
@@ -1,22 +1,36 @@
mod r#enum;
mod field;
mod r#struct;
mod r#union;

use proc_macro2::TokenStream;
use quote::format_ident;
use quote::ToTokens;
use syn::{Data, DeriveInput, Result};

use crate::validate::{
r#enum::validate_enum, r#struct::validate_struct_tokens, union::validate_union,
};
use crate::validate::{r#enum::ValidateEnum, r#struct::ValidateStruct, union::ValidateUnion};

pub fn validate_tokens(input: DeriveInput) -> Result<TokenStream> {
let ident = input.ident;
let error_ident = format_ident!("{ident}ValidationError");
pub enum Validate {
Struct(ValidateStruct),
Enum(ValidateEnum),
Union(ValidateUnion),
}

impl Validate {
pub fn parse(input: DeriveInput) -> Result<Self> {
Ok(match &input.data {
Data::Struct(data) => Self::Struct(ValidateStruct::parse(&input, data)?),
Data::Enum(data) => Self::Enum(ValidateEnum::parse(&input, data)?),
Data::Union(data) => Self::Union(ValidateUnion::parse(&input, data)?),
})
}
}

match input.data {
Data::Struct(data) => validate_struct_tokens(ident, error_ident, data),
Data::Enum(data) => validate_enum(ident, error_ident, data),
Data::Union(data) => validate_union(ident, error_ident, data),
impl ToTokens for Validate {
fn to_tokens(&self, tokens: &mut TokenStream) {
match self {
Validate::Struct(r#struct) => r#struct.to_tokens(tokens),
Validate::Enum(r#enum) => r#enum.to_tokens(tokens),
Validate::Union(r#union) => r#union.to_tokens(tokens),
}
}
}
17 changes: 14 additions & 3 deletions packages/fortifier-macros/src/validate/enum.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,17 @@
use proc_macro2::TokenStream;
use syn::{DataEnum, Ident, Result};
use quote::ToTokens;
use syn::{DataEnum, DeriveInput, Result};

pub fn validate_enum(_ident: Ident, _error_ident: Ident, _data: DataEnum) -> Result<TokenStream> {
todo!("enum")
pub struct ValidateEnum {}

impl ValidateEnum {
pub fn parse(_input: &DeriveInput, _data: &DataEnum) -> Result<Self> {
todo!("enum")
}
}

impl ToTokens for ValidateEnum {
fn to_tokens(&self, _tokens: &mut TokenStream) {
// TODO
}
}
68 changes: 68 additions & 0 deletions packages/fortifier-macros/src/validate/field.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
use proc_macro2::TokenStream;
use quote::quote;
use syn::{Field, Ident, Result};

use crate::validations::{Email, Length};

pub struct ValidateField {
ident: Ident,
// TODO: Consider using a trait for validations.
email: Option<Email>,
length: Option<Length>,
}

impl ValidateField {
pub fn parse(ident: Ident, field: &Field) -> Result<Self> {
let mut result = Self {
ident,
email: None,
length: None,
};

for attr in &field.attrs {
if attr.path().is_ident("validate") {
attr.parse_nested_meta(|meta| {
if meta.path.is_ident("email") {
result.email = Some(Email::parse(&meta)?);

Ok(())
} else if meta.path.is_ident("length") {
result.length = Some(Length::parse(&meta)?);

Ok(())
} else {
Err(meta.error("unknown validate parameter"))
}
})?;
}
}

Ok(result)
}

pub fn error_type(&self) -> TokenStream {
// TODO: Merge error types

if self.email.is_some() {
quote!(EmailError)
} else if self.length.is_some() {
quote!(LengthError<usize>)
} else {
quote!(())
}
}

pub fn sync_validations(&self) -> Vec<TokenStream> {
let email = self.email.as_ref().map(|email| email.tokens(&self.ident));
let length = self
.length
.as_ref()
.map(|length| length.tokens(&self.ident));

[email, length].into_iter().flatten().collect()
}

pub fn async_validations(&self) -> Vec<TokenStream> {
vec![]
}
}
Loading
Loading