diff --git a/packages/fortifier-macros/src/validate/struct.rs b/packages/fortifier-macros/src/validate/struct.rs index f10309f..c828954 100644 --- a/packages/fortifier-macros/src/validate/struct.rs +++ b/packages/fortifier-macros/src/validate/struct.rs @@ -3,7 +3,7 @@ use std::collections::HashMap; use convert_case::{Case, Casing}; use proc_macro2::{Literal, TokenStream}; use quote::{ToTokens, TokenStreamExt, format_ident, quote}; -use syn::{DataStruct, DeriveInput, Fields, FieldsNamed, FieldsUnnamed, Ident, Result}; +use syn::{DataStruct, DeriveInput, Fields, FieldsNamed, FieldsUnnamed, Generics, Ident, Result}; use crate::validate::field::ValidateField; @@ -38,6 +38,7 @@ impl ToTokens for ValidateStruct { pub struct ValidateNamedStruct { ident: Ident, error_ident: Ident, + generics: Generics, fields: HashMap, } @@ -46,6 +47,7 @@ impl ValidateNamedStruct { let mut result = Self { ident: input.ident.clone(), error_ident: format_ident!("{}ValidationError", input.ident), + generics: input.generics.clone(), fields: HashMap::default(), }; @@ -70,6 +72,8 @@ impl ToTokens for ValidateNamedStruct { fn to_tokens(&self, tokens: &mut TokenStream) { let ident = &self.ident; let error_ident = &self.error_ident; + let (impl_generics, type_generics, where_clause) = &self.generics.split_for_impl(); + let mut error_field_idents = vec![]; let mut error_field_types = vec![]; let mut error_field_enums = vec![]; @@ -127,7 +131,7 @@ impl ToTokens for ValidateNamedStruct { #(#error_field_enums)* #[automatically_derived] - impl Validate for #ident { + impl #impl_generics Validate for #ident #type_generics #where_clause { type Error = #error_ident; fn validate_sync(&self) -> Result<(), ValidationErrors> { @@ -163,6 +167,7 @@ impl ToTokens for ValidateNamedStruct { pub struct ValidateUnnamedStruct { ident: Ident, error_ident: Ident, + generics: Generics, fields: Vec, } @@ -171,6 +176,7 @@ impl ValidateUnnamedStruct { let mut result = Self { ident: input.ident.clone(), error_ident: format_ident!("{}ValidationError", input.ident), + generics: input.generics.clone(), fields: Vec::default(), }; @@ -195,6 +201,8 @@ impl ToTokens for ValidateUnnamedStruct { fn to_tokens(&self, tokens: &mut TokenStream) { let ident = &self.ident; let error_ident = &self.error_ident; + let (impl_generics, type_generics, where_clause) = &self.generics.split_for_impl(); + let mut error_field_idents = vec![]; let mut error_field_types = vec![]; let mut error_field_enums = vec![]; @@ -251,7 +259,7 @@ impl ToTokens for ValidateUnnamedStruct { #(#error_field_enums)* #[automatically_derived] - impl Validate for #ident { + impl #impl_generics Validate for #ident #type_generics #where_clause { type Error = #error_ident; fn validate_sync(&self) -> Result<(), ValidationErrors> { @@ -286,12 +294,14 @@ impl ToTokens for ValidateUnnamedStruct { pub struct ValidateUnitStruct { ident: Ident, + generics: Generics, } impl ValidateUnitStruct { fn parse(input: &DeriveInput) -> Result { Ok(Self { ident: input.ident.clone(), + generics: input.generics.clone(), }) } } @@ -299,12 +309,13 @@ impl ValidateUnitStruct { impl ToTokens for ValidateUnitStruct { fn to_tokens(&self, tokens: &mut TokenStream) { let ident = &self.ident; + let (impl_generics, type_generics, where_clause) = &self.generics.split_for_impl(); tokens.append_all(quote! { use fortifier::ValidationErrors; #[automatically_derived] - impl Validate for #ident { + impl #impl_generics Validate for #ident #type_generics #where_clause { type Error = ::std::convert::Infallible; fn validate_sync(&self) -> Result<(), ValidationErrors> { diff --git a/packages/fortifier-macros/tests/derive/struct_named_generics_pass.rs b/packages/fortifier-macros/tests/derive/struct_named_generics_pass.rs new file mode 100644 index 0000000..13df7e2 --- /dev/null +++ b/packages/fortifier-macros/tests/derive/struct_named_generics_pass.rs @@ -0,0 +1,23 @@ +use std::error::Error; + +use fortifier::Validate; + +#[derive(Validate)] +struct CreateUser> { + #[validate(email)] + email: E, + + #[validate(length(min = 1, max = 256))] + name: N, +} + +fn main() -> Result<(), Box> { + let data = CreateUser { + email: "john@doe.com", + name: "John Doe", + }; + + data.validate_sync()?; + + Ok(()) +} diff --git a/packages/fortifier-macros/tests/derive/struct_named_lifetimes_pass.rs b/packages/fortifier-macros/tests/derive/struct_named_lifetimes_pass.rs new file mode 100644 index 0000000..90f8d15 --- /dev/null +++ b/packages/fortifier-macros/tests/derive/struct_named_lifetimes_pass.rs @@ -0,0 +1,23 @@ +use std::error::Error; + +use fortifier::Validate; + +#[derive(Validate)] +struct CreateUser<'a, 'b> { + #[validate(email)] + email: &'a str, + + #[validate(length(min = 1, max = 256))] + name: &'b str, +} + +fn main() -> Result<(), Box> { + let data = CreateUser { + email: "john@doe.com", + name: "John Doe", + }; + + data.validate_sync()?; + + Ok(()) +} diff --git a/packages/fortifier-macros/tests/derive/struct_unnamed_generics_pass.rs b/packages/fortifier-macros/tests/derive/struct_unnamed_generics_pass.rs new file mode 100644 index 0000000..ce5e95e --- /dev/null +++ b/packages/fortifier-macros/tests/derive/struct_unnamed_generics_pass.rs @@ -0,0 +1,14 @@ +use std::error::Error; + +use fortifier::Validate; + +#[derive(Validate)] +struct CreateUser>(#[validate(length(min = 1, max = 256))] N); + +fn main() -> Result<(), Box> { + let data = CreateUser("John Doe"); + + data.validate_sync()?; + + Ok(()) +} diff --git a/packages/fortifier-macros/tests/derive/struct_unnamed_lifetimes_pass.rs b/packages/fortifier-macros/tests/derive/struct_unnamed_lifetimes_pass.rs new file mode 100644 index 0000000..250b0d6 --- /dev/null +++ b/packages/fortifier-macros/tests/derive/struct_unnamed_lifetimes_pass.rs @@ -0,0 +1,14 @@ +use std::error::Error; + +use fortifier::Validate; + +#[derive(Validate)] +struct CreateUser<'a>(#[validate(length(min = 1, max = 256))] &'a str); + +fn main() -> Result<(), Box> { + let data = CreateUser("John Doe"); + + data.validate_sync()?; + + Ok(()) +}