From 6f9e48672a4a8247617b9de8ee0901ebf592380e Mon Sep 17 00:00:00 2001 From: yassun7010 Date: Sat, 15 Jun 2024 02:22:28 +0900 Subject: [PATCH 01/10] wip: support warnings. --- serde_valid_derive/Cargo.toml | 1 + .../src/attribute/rule/named_struct_rule.rs | 24 +++- serde_valid_derive/src/derive.rs | 5 +- serde_valid_derive/src/derive/enum_derive.rs | 109 +++++++++++------- .../src/derive/named_struct_derive.rs | 46 +++++--- .../src/derive/unnamed_struct_derive.rs | 38 +++--- serde_valid_derive/src/lib.rs | 17 ++- serde_valid_derive/src/output_stream.rs | 24 ++++ serde_valid_derive/src/warning.rs | 3 + 9 files changed, 186 insertions(+), 81 deletions(-) create mode 100644 serde_valid_derive/src/output_stream.rs create mode 100644 serde_valid_derive/src/warning.rs diff --git a/serde_valid_derive/Cargo.toml b/serde_valid_derive/Cargo.toml index d5e2897..7142e57 100644 --- a/serde_valid_derive/Cargo.toml +++ b/serde_valid_derive/Cargo.toml @@ -18,6 +18,7 @@ proc-macro = true [dependencies] paste = { workspace = true } proc-macro-error = "^1.0" +proc-macro-warning = "1.0.2" proc-macro2 = "^1.0" quote = "^1.0" strsim = "0.11.0" diff --git a/serde_valid_derive/src/attribute/rule/named_struct_rule.rs b/serde_valid_derive/src/attribute/rule/named_struct_rule.rs index 493d0ee..b750df7 100644 --- a/serde_valid_derive/src/attribute/rule/named_struct_rule.rs +++ b/serde_valid_derive/src/attribute/rule/named_struct_rule.rs @@ -1,20 +1,32 @@ use std::collections::HashSet; use proc_macro2::TokenStream; +use proc_macro_warning::FormattedWarning; use quote::{quote, ToTokens}; use syn::spanned::Spanned; -use crate::types::{CommaSeparatedNestedMetas, CommaSeparatedTokenStreams}; +use crate::{ + output_stream::OutputStream, + types::{CommaSeparatedNestedMetas, CommaSeparatedTokenStreams}, +}; pub fn collect_rules_from_named_struct( attributes: &[syn::Attribute], -) -> Result<(HashSet, TokenStream), crate::Errors> { +) -> Result<(HashSet, OutputStream), crate::Errors> { let mut errors = vec![]; let mut rule_fields = HashSet::new(); + let mut warnings = vec![]; let rules = attributes .iter() .filter(|attribute| attribute.path().is_ident("rule")) + .inspect(|attribute| { + warnings.push(FormattedWarning::new_deprecated( + "rule", + "#[rule(...)] is deprecated, use #[validate(custom(...)))] instead", + attribute.bracket_token.span.span(), + )); + }) .filter_map(|attribute| match &attribute.meta { syn::Meta::List(list) => match collect_rule(list) { Ok((field_ident, stream)) => { @@ -36,7 +48,13 @@ pub fn collect_rules_from_named_struct( .collect::>(); if errors.is_empty() { - Ok((rule_fields, TokenStream::from_iter(rules))) + Ok(( + rule_fields, + OutputStream { + output: TokenStream::from_iter(rules), + warnings, + }, + )) } else { Err(errors) } diff --git a/serde_valid_derive/src/derive.rs b/serde_valid_derive/src/derive.rs index 9067bcc..184672b 100644 --- a/serde_valid_derive/src/derive.rs +++ b/serde_valid_derive/src/derive.rs @@ -4,10 +4,11 @@ mod unnamed_struct_derive; use enum_derive::expand_enum_validate_derive; use named_struct_derive::expand_named_struct_derive; -use proc_macro2::TokenStream; use unnamed_struct_derive::expand_unnamed_struct_derive; -pub fn expand_derive(input: &syn::DeriveInput) -> Result { +use crate::output_stream::OutputStream; + +pub fn expand_derive(input: &syn::DeriveInput) -> Result { match &input.data { syn::Data::Struct(syn::DataStruct { ref fields, .. }) => match fields { syn::Fields::Named(fields) => expand_named_struct_derive(input, fields), diff --git a/serde_valid_derive/src/derive/enum_derive.rs b/serde_valid_derive/src/derive/enum_derive.rs index c25a0c8..1acc113 100644 --- a/serde_valid_derive/src/derive/enum_derive.rs +++ b/serde_valid_derive/src/derive/enum_derive.rs @@ -2,6 +2,7 @@ use super::named_struct_derive::collect_named_fields_validators_list; use super::unnamed_struct_derive::collect_unnamed_fields_validators_list; use crate::attribute::rule::{collect_rules_from_named_struct, collect_rules_from_unnamed_struct}; use crate::error::{array_errors_tokens, new_type_errors_tokens, object_errors_tokens}; +use crate::output_stream::OutputStream; use crate::serde::rename::collect_serde_rename_map; use crate::types::CommaSeparatedTokenStreams; use proc_macro2::TokenStream; @@ -14,20 +15,21 @@ pub type Variants = syn::punctuated::Punctuated pub fn expand_enum_validate_derive( input: &syn::DeriveInput, variants: &Variants, -) -> Result { +) -> Result { let ident = &input.ident; let (impl_generics, type_generics, where_clause) = input.generics.split_for_impl(); let mut errors = vec![]; - let validations_and_rules = - TokenStream::from_iter(variants.iter().map(|variant| match &variant.fields { + let validations = variants + .into_iter() + .map(|variant| match &variant.fields { syn::Fields::Named(named_fields) => { - match expand_enum_variant_named_fields(ident, variant, named_fields) { + match expand_enum_variant_named_fields_validation(ident, variant, named_fields) { Ok(variant_varidates_and_rules) => variant_varidates_and_rules, Err(variant_errors) => { errors.extend(variant_errors); - quote!() + OutputStream::new() } } } @@ -37,44 +39,65 @@ pub fn expand_enum_validate_derive( Ok(variant_varidates_and_rules) => variant_varidates_and_rules, Err(variant_errors) => { errors.extend(variant_errors); - quote!() + OutputStream::new() } } } - syn::Fields::Unit => quote!(), - })); + syn::Fields::Unit => OutputStream::new(), + }) + .collect::>(); + + let validations_and_rules = TokenStream::from_iter( + validations + .iter() + .map(|variant| variant.output.clone()) + .collect::>(), + ); + let warnings = validations + .into_iter() + .flat_map(|variant| variant.warnings) + .collect::>(); if errors.is_empty() { - Ok(quote!( - impl #impl_generics ::serde_valid::Validate for #ident #type_generics #where_clause { - fn validate(&self) -> std::result::Result<(), ::serde_valid::validation::Errors> { - #validations_and_rules + Ok(OutputStream { + output: quote!( + impl #impl_generics ::serde_valid::Validate for #ident #type_generics #where_clause { + fn validate(&self) -> std::result::Result<(), ::serde_valid::validation::Errors> { + #validations_and_rules - Ok(()) + Ok(()) + } } - } - )) + ), + warnings, + }) } else { Err(errors) } } -fn expand_enum_variant_named_fields( +fn expand_enum_variant_named_fields_validation( ident: &syn::Ident, variant: &syn::Variant, named_fields: &syn::FieldsNamed, -) -> Result { +) -> Result { let mut errors = vec![]; let variant_ident = &variant.ident; let mut fields_idents = CommaSeparatedTokenStreams::new(); let rename_map = collect_serde_rename_map(named_fields); - let (rule_fields, rules) = match collect_rules_from_named_struct(&variant.attrs) { + let ( + rule_fields, + OutputStream { + output: rules, + warnings, + }, + ) = match collect_rules_from_named_struct(&variant.attrs) { Ok(field_rules) => field_rules, Err(variant_errors) => { errors.extend(variant_errors); - (HashSet::new(), quote!()) + (HashSet::new(), OutputStream::new()) } }; @@ -105,19 +128,22 @@ fn expand_enum_variant_named_fields( let variant_errors = object_errors_tokens(); if errors.is_empty() { - Ok(quote!( - if let #ident::#variant_ident{#fields_idents} = &self { - let mut __rule_vec_errors = ::serde_valid::validation::VecErrors::new(); - let mut __property_vec_errors_map = ::serde_valid::validation::PropertyVecErrorsMap::new(); + Ok(OutputStream { + output: quote!( + if let #ident::#variant_ident{#fields_idents} = &self { + let mut __rule_vec_errors = ::serde_valid::validation::VecErrors::new(); + let mut __property_vec_errors_map = ::serde_valid::validation::PropertyVecErrorsMap::new(); - #validates - #rules + #validates + #rules - if !(__rule_vec_errors.is_empty() && __property_vec_errors_map.is_empty()) { - Err(#variant_errors)? + if !(__rule_vec_errors.is_empty() && __property_vec_errors_map.is_empty()) { + Err(#variant_errors)? + } } - } - )) + ), + warnings, + }) } else { Err(errors) } @@ -127,7 +153,7 @@ fn expand_enum_variant_unnamed_fields_varidation( ident: &syn::Ident, variant: &syn::Variant, unnamed_fields: &syn::FieldsUnnamed, -) -> Result { +) -> Result { let mut errors = vec![]; let variant_ident = &variant.ident; @@ -172,19 +198,22 @@ fn expand_enum_variant_unnamed_fields_varidation( }; if errors.is_empty() { - Ok(quote!( - if let #ident::#variant_ident(#fields_idents) = &self { - let mut __rule_vec_errors = ::serde_valid::validation::VecErrors::new(); - let mut __item_vec_errors_map = ::serde_valid::validation::ItemVecErrorsMap::new(); + Ok(OutputStream { + output: quote!( + if let #ident::#variant_ident(#fields_idents) = &self { + let mut __rule_vec_errors = ::serde_valid::validation::VecErrors::new(); + let mut __item_vec_errors_map = ::serde_valid::validation::ItemVecErrorsMap::new(); - #validates - #rules + #validates + #rules - if !(__rule_vec_errors.is_empty() && __item_vec_errors_map.is_empty()) { - Err(#variant_errors)? + if !(__rule_vec_errors.is_empty() && __item_vec_errors_map.is_empty()) { + Err(#variant_errors)? + } } - } - )) + ), + warnings: vec![], + }) } else { Err(errors) } diff --git a/serde_valid_derive/src/derive/named_struct_derive.rs b/serde_valid_derive/src/derive/named_struct_derive.rs index 616a5d2..a0f2b4b 100644 --- a/serde_valid_derive/src/derive/named_struct_derive.rs +++ b/serde_valid_derive/src/derive/named_struct_derive.rs @@ -2,6 +2,7 @@ use crate::attribute::field_validate::{extract_field_validator, FieldValidators} use crate::attribute::rule::collect_rules_from_named_struct; use crate::attribute::struct_validate::collect_struct_custom_from_named_struct; use crate::error::object_errors_tokens; +use crate::output_stream::OutputStream; use crate::serde::rename::{collect_serde_rename_map, RenameMap}; use crate::types::{Field, NamedField}; use proc_macro2::TokenStream; @@ -13,18 +14,24 @@ use std::iter::FromIterator; pub fn expand_named_struct_derive( input: &syn::DeriveInput, fields: &syn::FieldsNamed, -) -> Result { +) -> Result { let ident = &input.ident; let (impl_generics, type_generics, where_clause) = input.generics.split_for_impl(); let rename_map = collect_serde_rename_map(fields); let mut errors = vec![]; - let (rule_fields, rules) = match collect_rules_from_named_struct(&input.attrs) { - Ok((rule_fields, rules)) => (rule_fields, TokenStream::from_iter(rules)), + let ( + rule_fields, + OutputStream { + output: rules, + warnings, + }, + ) = match collect_rules_from_named_struct(&input.attrs) { + Ok((rule_fields, rules)) => (rule_fields, rules), Err(rule_errors) => { errors.extend(rule_errors); - (HashSet::new(), quote!()) + (HashSet::new(), OutputStream::new()) } }; let struct_validations = match collect_struct_custom_from_named_struct(&input.attrs) { @@ -52,24 +59,27 @@ pub fn expand_named_struct_derive( let fields_errors = object_errors_tokens(); if errors.is_empty() { - Ok(quote!( - impl #impl_generics ::serde_valid::Validate for #ident #type_generics #where_clause { - fn validate(&self) -> std::result::Result<(), ::serde_valid::validation::Errors> { - let mut __rule_vec_errors = ::serde_valid::validation::VecErrors::new(); - let mut __property_vec_errors_map = ::serde_valid::validation::PropertyVecErrorsMap::new(); + Ok(OutputStream { + output: quote!( + impl #impl_generics ::serde_valid::Validate for #ident #type_generics #where_clause { + fn validate(&self) -> std::result::Result<(), ::serde_valid::validation::Errors> { + let mut __rule_vec_errors = ::serde_valid::validation::VecErrors::new(); + let mut __property_vec_errors_map = ::serde_valid::validation::PropertyVecErrorsMap::new(); - #field_validates - #struct_validations - #rules + #field_validates + #struct_validations + #rules - if __rule_vec_errors.is_empty() && __property_vec_errors_map.is_empty() { - Ok(()) - } else { - Err(#fields_errors) + if __rule_vec_errors.is_empty() && __property_vec_errors_map.is_empty() { + Ok(()) + } else { + Err(#fields_errors) + } } } - } - )) + ), + warnings, + }) } else { Err(errors) } diff --git a/serde_valid_derive/src/derive/unnamed_struct_derive.rs b/serde_valid_derive/src/derive/unnamed_struct_derive.rs index a07423f..910e7a3 100644 --- a/serde_valid_derive/src/derive/unnamed_struct_derive.rs +++ b/serde_valid_derive/src/derive/unnamed_struct_derive.rs @@ -2,6 +2,7 @@ use crate::attribute::field_validate::{extract_field_validator, FieldValidators} use crate::attribute::rule::collect_rules_from_unnamed_struct; use crate::attribute::struct_validate::collect_struct_custom_from_named_struct; use crate::error::{array_errors_tokens, new_type_errors_tokens}; +use crate::output_stream::OutputStream; use crate::types::{Field, UnnamedField}; use proc_macro2::TokenStream; use quote::quote; @@ -12,7 +13,7 @@ use std::iter::FromIterator; pub fn expand_unnamed_struct_derive( input: &syn::DeriveInput, fields: &syn::FieldsUnnamed, -) -> Result { +) -> Result { let ident = &input.ident; let (impl_generics, type_generics, where_clause) = input.generics.split_for_impl(); @@ -55,24 +56,27 @@ pub fn expand_unnamed_struct_derive( }; if errors.is_empty() { - Ok(quote!( - impl #impl_generics ::serde_valid::Validate for #ident #type_generics #where_clause { - fn validate(&self) -> std::result::Result<(), ::serde_valid::validation::Errors> { - let mut __rule_vec_errors = ::serde_valid::validation::VecErrors::new(); - let mut __item_vec_errors_map = ::serde_valid::validation::ItemVecErrorsMap::new(); - - #field_validates - #struct_validations - #rules - - if __rule_vec_errors.is_empty() && __item_vec_errors_map.is_empty() { - Ok(()) - } else { - Err(#fields_errors) + Ok(OutputStream { + output: quote!( + impl #impl_generics ::serde_valid::Validate for #ident #type_generics #where_clause { + fn validate(&self) -> std::result::Result<(), ::serde_valid::validation::Errors> { + let mut __rule_vec_errors = ::serde_valid::validation::VecErrors::new(); + let mut __item_vec_errors_map = ::serde_valid::validation::ItemVecErrorsMap::new(); + + #field_validates + #struct_validations + #rules + + if __rule_vec_errors.is_empty() && __item_vec_errors_map.is_empty() { + Ok(()) + } else { + Err(#fields_errors) + } } } - } - )) + ), + warnings: vec![], + }) } else { Err(errors) } diff --git a/serde_valid_derive/src/lib.rs b/serde_valid_derive/src/lib.rs index eaf8dbe..649d897 100644 --- a/serde_valid_derive/src/lib.rs +++ b/serde_valid_derive/src/lib.rs @@ -2,14 +2,18 @@ mod attribute; mod derive; mod error; +mod output_stream; mod serde; mod types; +mod warning; use derive::expand_derive; use error::to_compile_errors; use error::{Error, Errors}; +use output_stream::OutputStream; use proc_macro::TokenStream; use proc_macro_error::proc_macro_error; +use quote::quote; use syn::{parse_macro_input, DeriveInput}; #[proc_macro_derive(Validate, attributes(rule, validate, serde_valid))] @@ -18,6 +22,17 @@ pub fn derive_validate(tokens: TokenStream) -> TokenStream { let input = parse_macro_input!(tokens as DeriveInput); expand_derive(&input) - .unwrap_or_else(to_compile_errors) + .map_or_else(to_compile_errors, |OutputStream { output, warnings }| { + let warnings = + proc_macro2::TokenStream::from_iter(warnings.into_iter().map(|warning| { + quote!( + #warning + ) + })); + quote!( + #output + #warnings + ) + }) .into() } diff --git a/serde_valid_derive/src/output_stream.rs b/serde_valid_derive/src/output_stream.rs new file mode 100644 index 0000000..20dd41d --- /dev/null +++ b/serde_valid_derive/src/output_stream.rs @@ -0,0 +1,24 @@ +use proc_macro2::TokenStream; + +use crate::warning::Warning; + +#[derive(Debug, Clone)] +pub struct OutputStream { + pub output: TokenStream, + pub warnings: Vec, +} + +impl OutputStream { + pub fn new() -> Self { + Self { + output: TokenStream::new(), + warnings: vec![], + } + } + + #[allow(unused)] + pub fn extend_warnings(&mut self, warnings: Vec) -> &mut Self { + self.warnings.extend(warnings); + self + } +} diff --git a/serde_valid_derive/src/warning.rs b/serde_valid_derive/src/warning.rs new file mode 100644 index 0000000..e4c239f --- /dev/null +++ b/serde_valid_derive/src/warning.rs @@ -0,0 +1,3 @@ +use proc_macro_warning::FormattedWarning; + +pub type Warning = FormattedWarning; From 5f286cf33c4e99ae973b80fb45e307323185a287 Mon Sep 17 00:00:00 2001 From: yassun7010 Date: Sat, 15 Jun 2024 12:13:47 +0900 Subject: [PATCH 02/10] refactor: --- serde_valid_derive/src/lib.rs | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/serde_valid_derive/src/lib.rs b/serde_valid_derive/src/lib.rs index 649d897..a29059a 100644 --- a/serde_valid_derive/src/lib.rs +++ b/serde_valid_derive/src/lib.rs @@ -23,15 +23,11 @@ pub fn derive_validate(tokens: TokenStream) -> TokenStream { expand_derive(&input) .map_or_else(to_compile_errors, |OutputStream { output, warnings }| { - let warnings = - proc_macro2::TokenStream::from_iter(warnings.into_iter().map(|warning| { - quote!( - #warning - ) - })); quote!( #output - #warnings + #( + #warnings + )* ) }) .into() From dccfcbf6d1075062d0254ad18f7a65e26c4cebb0 Mon Sep 17 00:00:00 2001 From: yassun7010 Date: Sat, 15 Jun 2024 12:14:08 +0900 Subject: [PATCH 03/10] chore: use new method. --- serde_valid/tests/array_test.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/serde_valid/tests/array_test.rs b/serde_valid/tests/array_test.rs index c98c794..3e00eda 100644 --- a/serde_valid/tests/array_test.rs +++ b/serde_valid/tests/array_test.rs @@ -3,7 +3,7 @@ use serde_valid::Validate; #[test] fn items_err_message() { - fn rule_sample(_a: &i32) -> Result<(), serde_valid::validation::Error> { + fn rule_sample(_a: i32) -> Result<(), serde_valid::validation::Error> { Err(serde_valid::validation::Error::Custom( "Rule error.".to_owned(), )) @@ -18,7 +18,7 @@ fn items_err_message() { } #[derive(Validate)] - #[rule(rule_sample(val))] + #[validate(custom(|s| rule_sample(s.val)))] struct TestChildStruct { #[validate(minimum = 1)] #[validate(maximum = 10)] From 76f06974f50258c45d21e44fc292a3e6d0047bb8 Mon Sep 17 00:00:00 2001 From: yassun7010 Date: Sat, 15 Jun 2024 12:14:20 +0900 Subject: [PATCH 04/10] chore: use new method --- serde_valid/tests/complex_test.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/serde_valid/tests/complex_test.rs b/serde_valid/tests/complex_test.rs index 082e57c..e517845 100644 --- a/serde_valid/tests/complex_test.rs +++ b/serde_valid/tests/complex_test.rs @@ -1,11 +1,11 @@ use serde_valid::Validate; -fn sample_rule(_val: &i32) -> Result<(), serde_valid::validation::Error> { +fn sample_rule(_val: i32) -> Result<(), serde_valid::validation::Error> { Ok(()) } #[derive(Debug, Validate)] -#[rule(sample_rule(int_value))] +#[validate(custom(|s| sample_rule(s.int_value)))] struct TestStruct<'a> { // Generic validator #[validate(enumerate(5, 10, 15))] From 8eb5de12afe064531b75148459870a36eef8fa39 Mon Sep 17 00:00:00 2001 From: yassun7010 Date: Sat, 15 Jun 2024 12:23:43 +0900 Subject: [PATCH 05/10] feat: add enum variant custom validation. --- serde_valid_derive/src/attribute.rs | 1 + .../src/attribute/variant_validate.rs | 35 +++++ .../src/attribute/variant_validate/generic.rs | 3 + .../variant_validate/generic/custom.rs | 82 ++++++++++ .../src/attribute/variant_validate/meta.rs | 141 ++++++++++++++++++ .../variant_validate/meta/meta_list.rs | 17 +++ .../variant_validate/meta/meta_name_value.rs | 12 ++ .../variant_validate/meta/meta_path.rs | 12 ++ serde_valid_derive/src/derive/enum_derive.rs | 10 ++ 9 files changed, 313 insertions(+) create mode 100644 serde_valid_derive/src/attribute/variant_validate.rs create mode 100644 serde_valid_derive/src/attribute/variant_validate/generic.rs create mode 100644 serde_valid_derive/src/attribute/variant_validate/generic/custom.rs create mode 100644 serde_valid_derive/src/attribute/variant_validate/meta.rs create mode 100644 serde_valid_derive/src/attribute/variant_validate/meta/meta_list.rs create mode 100644 serde_valid_derive/src/attribute/variant_validate/meta/meta_name_value.rs create mode 100644 serde_valid_derive/src/attribute/variant_validate/meta/meta_path.rs diff --git a/serde_valid_derive/src/attribute.rs b/serde_valid_derive/src/attribute.rs index 683a6c3..5c77386 100644 --- a/serde_valid_derive/src/attribute.rs +++ b/serde_valid_derive/src/attribute.rs @@ -4,6 +4,7 @@ pub mod common; pub mod field_validate; pub mod rule; pub mod struct_validate; +pub mod variant_validate; pub type Validator = TokenStream; diff --git a/serde_valid_derive/src/attribute/variant_validate.rs b/serde_valid_derive/src/attribute/variant_validate.rs new file mode 100644 index 0000000..cf2b4e1 --- /dev/null +++ b/serde_valid_derive/src/attribute/variant_validate.rs @@ -0,0 +1,35 @@ +mod generic; +mod meta; + +use crate::attribute::Validator; + +use self::meta::extract_variant_validator; + +pub fn collect_variant_custom_from_named_variant( + attributes: &[syn::Attribute], +) -> Result { + let mut errors = vec![]; + + let validations = attributes + .iter() + .filter_map(|attribute| { + if attribute.path().is_ident("validate") { + match extract_variant_validator(attribute) { + Ok(validator) => Some(validator), + Err(validator_error) => { + errors.extend(validator_error); + None + } + } + } else { + None + } + }) + .collect::>(); + + if errors.is_empty() { + Ok(Validator::from_iter(validations)) + } else { + Err(errors) + } +} diff --git a/serde_valid_derive/src/attribute/variant_validate/generic.rs b/serde_valid_derive/src/attribute/variant_validate/generic.rs new file mode 100644 index 0000000..49e6c40 --- /dev/null +++ b/serde_valid_derive/src/attribute/variant_validate/generic.rs @@ -0,0 +1,3 @@ +mod custom; + +pub use custom::extract_generic_variant_custom_validator; diff --git a/serde_valid_derive/src/attribute/variant_validate/generic/custom.rs b/serde_valid_derive/src/attribute/variant_validate/generic/custom.rs new file mode 100644 index 0000000..46d0b8f --- /dev/null +++ b/serde_valid_derive/src/attribute/variant_validate/generic/custom.rs @@ -0,0 +1,82 @@ +use crate::attribute::common::message_format::MessageFormat; +use crate::attribute::Validator; +use crate::types::CommaSeparatedNestedMetas; +use quote::quote; + +pub fn extract_generic_variant_custom_validator( + meta_list: &syn::MetaList, + _message_format: MessageFormat, +) -> Result { + let mut errors = vec![]; + + let nested = meta_list + .parse_args_with(CommaSeparatedNestedMetas::parse_terminated) + .map_err(|error| vec![crate::Error::rule_args_parse_error(meta_list, &error)])?; + + match nested.len() { + 0 => Err(vec![ + crate::Error::validate_custom_need_function_or_closure(meta_list), + ])?, + 2.. => nested + .iter() + .skip(1) + .for_each(|error| errors.push(crate::Error::rule_allow_single_function(error))), + _ => {} + } + + let rule = match &nested[0] { + crate::types::NestedMeta::Meta(syn::Meta::Path(path)) => { + extract_variant_custom_from_meta_path(path) + } + crate::types::NestedMeta::Meta(syn::Meta::List(list)) => { + extract_variant_custom_from_meta_list(list) + } + crate::types::NestedMeta::Closure(closure) => extract_variant_custom_from_closure(closure), + _ => Err(vec![ + crate::Error::validate_custom_need_function_or_closure(&nested[0]), + ]), + }; + + match rule { + Ok(_) => { + if errors.is_empty() { + rule + } else { + Err(errors) + } + } + Err(rule_errors) => Err(errors.into_iter().chain(rule_errors).collect()), + } +} + +fn extract_variant_custom_from_meta_path( + meta_path: &syn::Path, +) -> Result { + let rule_fn_name = &meta_path; + + Ok(quote!( + if let Err(__error) = #rule_fn_name(self) { + __rule_vec_errors.push(__error); + }; + )) +} + +fn extract_variant_custom_from_meta_list( + meta_list: &syn::MetaList, +) -> Result { + Ok(quote!( + if let Err(__error) = serde_valid::helpers::wrap_closure_validation(self, #meta_list) { + __rule_vec_errors.push(__error); + }; + )) +} + +fn extract_variant_custom_from_closure( + closure: &syn::ExprClosure, +) -> Result { + Ok(quote!( + if let Err(__error) = serde_valid::helpers::wrap_closure_validation(self, #closure) { + __rule_vec_errors.push(__error); + }; + )) +} diff --git a/serde_valid_derive/src/attribute/variant_validate/meta.rs b/serde_valid_derive/src/attribute/variant_validate/meta.rs new file mode 100644 index 0000000..972b326 --- /dev/null +++ b/serde_valid_derive/src/attribute/variant_validate/meta.rs @@ -0,0 +1,141 @@ +mod meta_list; +mod meta_name_value; +mod meta_path; + +use crate::{ + attribute::{ + common::message_format::{default_message_format, extract_custom_message_format}, + MetaListStructValidation, MetaNameValueStructValidation, MetaPathStructValidation, + Validator, + }, + types::SingleIdentPath, +}; +use quote::quote; +use std::str::FromStr; + +use self::{ + meta_list::extract_variant_validator_from_meta_list, + meta_name_value::extract_variant_validator_from_meta_name_value, + meta_path::extract_variant_validator_from_meta_path, +}; + +pub fn extract_variant_validator(attribute: &syn::Attribute) -> Result { + match &attribute.meta { + syn::Meta::Path(_) => Ok(quote!()), + syn::Meta::List(list) => inner_extract_variant_validator(attribute, list), + syn::Meta::NameValue(name_value) => { + Err(vec![crate::Error::validate_meta_name_value_not_supported( + name_value, + )]) + } + } +} + +fn inner_extract_variant_validator( + attribute: &syn::Attribute, + meta_list: &syn::MetaList, +) -> Result { + let mut errors = vec![]; + let nested = meta_list + .parse_args_with(crate::types::CommaSeparatedMetas::parse_terminated) + .map_err(|error| { + vec![crate::Error::validate_attribute_parse_error( + attribute, &error, + )] + })?; + + let message_format = match nested.len() { + 0 => Err(vec![crate::Error::struct_validation_type_required( + attribute, + )])?, + 1 => None, + 2 => match extract_custom_message_format(&nested[1]) { + Ok(custom_message) => { + if nested[0].path().is_ident("custom") { + errors.push( + crate::Error::validate_custom_does_not_support_custom_message(&nested[1]), + ); + None + } else { + Some(custom_message) + } + } + Err(message_fn_errors) => { + errors.extend(message_fn_errors); + None + } + }, + _ => { + for meta in nested.iter().skip(2) { + errors.push(crate::Error::too_many_list_items(meta)); + } + None + } + } + .unwrap_or_else(default_message_format); + + let meta = &nested[0]; + let validation_path = match meta { + syn::Meta::Path(path) => path, + syn::Meta::List(list) => &list.path, + syn::Meta::NameValue(name_value) => &name_value.path, + }; + + let validation_name = SingleIdentPath::new(validation_path).ident().to_string(); + let validator = match ( + MetaPathStructValidation::from_str(&validation_name), + MetaListStructValidation::from_str(&validation_name), + MetaNameValueStructValidation::from_str(&validation_name), + meta, + ) { + (Ok(validation_type), _, _, syn::Meta::Path(validation)) => { + extract_variant_validator_from_meta_path(validation_type, validation, message_format) + } + + (_, Ok(validation_type), _, syn::Meta::List(validation)) => { + extract_variant_validator_from_meta_list(validation_type, validation, message_format) + } + + (_, _, Ok(validation_type), syn::Meta::NameValue(validation)) => { + extract_variant_validator_from_meta_name_value( + validation_type, + validation, + message_format, + ) + } + + (Ok(_), _, _, _) => Err(vec![crate::Error::meta_path_validation_need_value( + validation_path, + &validation_name, + )]), + + (_, Ok(_), _, _) => Err(vec![crate::Error::meta_list_validation_need_value( + validation_path, + &validation_name, + )]), + + (_, _, Ok(_), _) => Err(vec![crate::Error::meta_name_value_validation_need_value( + validation_path, + &validation_name, + )]), + + _ => Err(vec![crate::Error::struct_validation_type_unknown( + validation_path, + &validation_name, + )]), + }; + + match validator { + Ok(validator) => { + if errors.is_empty() { + Ok(validator) + } else { + Err(errors) + } + } + Err(validator_errors) => { + errors.extend(validator_errors); + Err(errors) + } + } +} diff --git a/serde_valid_derive/src/attribute/variant_validate/meta/meta_list.rs b/serde_valid_derive/src/attribute/variant_validate/meta/meta_list.rs new file mode 100644 index 0000000..6608771 --- /dev/null +++ b/serde_valid_derive/src/attribute/variant_validate/meta/meta_list.rs @@ -0,0 +1,17 @@ +use crate::attribute::{ + common::message_format::MessageFormat, + variant_validate::generic::extract_generic_variant_custom_validator, MetaListStructValidation, + Validator, +}; + +pub fn extract_variant_validator_from_meta_list( + validation_type: MetaListStructValidation, + validation: &syn::MetaList, + message_format: MessageFormat, +) -> Result { + match validation_type { + MetaListStructValidation::Custom => { + extract_generic_variant_custom_validator(validation, message_format) + } + } +} diff --git a/serde_valid_derive/src/attribute/variant_validate/meta/meta_name_value.rs b/serde_valid_derive/src/attribute/variant_validate/meta/meta_name_value.rs new file mode 100644 index 0000000..3b52bd3 --- /dev/null +++ b/serde_valid_derive/src/attribute/variant_validate/meta/meta_name_value.rs @@ -0,0 +1,12 @@ +use crate::attribute::{ + common::message_format::MessageFormat, MetaNameValueStructValidation, Validator, +}; + +#[inline] +pub fn extract_variant_validator_from_meta_name_value( + validation_type: MetaNameValueStructValidation, + _validation: &syn::MetaNameValue, + _message_format: MessageFormat, +) -> Result { + match validation_type {} +} diff --git a/serde_valid_derive/src/attribute/variant_validate/meta/meta_path.rs b/serde_valid_derive/src/attribute/variant_validate/meta/meta_path.rs new file mode 100644 index 0000000..bec8f8c --- /dev/null +++ b/serde_valid_derive/src/attribute/variant_validate/meta/meta_path.rs @@ -0,0 +1,12 @@ +use crate::attribute::{ + common::message_format::MessageFormat, MetaPathStructValidation, Validator, +}; + +#[inline] +pub fn extract_variant_validator_from_meta_path( + validation_type: MetaPathStructValidation, + _validation: &syn::Path, + _message_format: MessageFormat, +) -> Result { + match validation_type {} +} diff --git a/serde_valid_derive/src/derive/enum_derive.rs b/serde_valid_derive/src/derive/enum_derive.rs index 1acc113..bcf3480 100644 --- a/serde_valid_derive/src/derive/enum_derive.rs +++ b/serde_valid_derive/src/derive/enum_derive.rs @@ -1,6 +1,7 @@ use super::named_struct_derive::collect_named_fields_validators_list; use super::unnamed_struct_derive::collect_unnamed_fields_validators_list; use crate::attribute::rule::{collect_rules_from_named_struct, collect_rules_from_unnamed_struct}; +use crate::attribute::variant_validate::collect_variant_custom_from_named_variant; use crate::error::{array_errors_tokens, new_type_errors_tokens, object_errors_tokens}; use crate::output_stream::OutputStream; use crate::serde::rename::collect_serde_rename_map; @@ -101,6 +102,14 @@ fn expand_enum_variant_named_fields_validation( } }; + let enum_validations = match collect_variant_custom_from_named_variant(&variant.attrs) { + Ok(validations) => TokenStream::from_iter(validations), + Err(rule_errors) => { + errors.extend(rule_errors); + quote!() + } + }; + let validates = match collect_named_fields_validators_list(named_fields, &rename_map) { Ok(field_validators_list) => { TokenStream::from_iter(field_validators_list.iter().map(|validators| { @@ -135,6 +144,7 @@ fn expand_enum_variant_named_fields_validation( let mut __property_vec_errors_map = ::serde_valid::validation::PropertyVecErrorsMap::new(); #validates + #enum_validations #rules if !(__rule_vec_errors.is_empty() && __property_vec_errors_map.is_empty()) { From 3ed253d02848becd675deef0d72ae4150c4ebc00 Mon Sep 17 00:00:00 2001 From: yassun7010 Date: Sat, 15 Jun 2024 12:28:37 +0900 Subject: [PATCH 06/10] chore: use new method. --- serde_valid/tests/specific_test.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/serde_valid/tests/specific_test.rs b/serde_valid/tests/specific_test.rs index d877519..65e3bae 100644 --- a/serde_valid/tests/specific_test.rs +++ b/serde_valid/tests/specific_test.rs @@ -3,13 +3,13 @@ use serde_valid::Validate; #[test] fn test_raw_type_field() { #[derive(Validate)] - #[rule(sample_rule(r#type))] + #[validate(custom(|s| sample_rule(s.r#type)))] struct MyStruct { #[validate(maximum = 10)] pub r#type: i32, } - fn sample_rule(_type: &i32) -> Result<(), serde_valid::validation::Error> { + fn sample_rule(_type: i32) -> Result<(), serde_valid::validation::Error> { Ok(()) } From 512aea1538d50122fdf6e3b63b5cb748828944ac Mon Sep 17 00:00:00 2001 From: yassun7010 Date: Sat, 15 Jun 2024 12:40:38 +0900 Subject: [PATCH 07/10] feat: add enum validation. --- serde_valid/tests/enum_test.rs | 8 ++++---- serde_valid_derive/src/derive/enum_derive.rs | 13 ++++++++++--- 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/serde_valid/tests/enum_test.rs b/serde_valid/tests/enum_test.rs index 6ee2f73..f7dd04f 100644 --- a/serde_valid/tests/enum_test.rs +++ b/serde_valid/tests/enum_test.rs @@ -3,12 +3,12 @@ use serde_valid::Validate; #[test] fn enum_named_variant_validation_is_ok() { - fn ok_rule(_a: &TestStruct, _b: &TestStruct) -> Result<(), serde_valid::validation::Error> { + fn ok_rule(_value: &TestEnum) -> Result<(), serde_valid::validation::Error> { Ok(()) } #[derive(Validate)] + #[validate(custom(ok_rule))] enum TestEnum { - #[rule(ok_rule(a, b))] Named { #[validate] a: TestStruct, @@ -71,15 +71,15 @@ fn enum_newtype_variant_validation_is_ok() { #[test] fn enum_named_variant_validation_is_err() { - fn err_rule(_a: &TestStruct, _b: &TestStruct) -> Result<(), serde_valid::validation::Error> { + fn err_rule(_data: &TestEnum) -> Result<(), serde_valid::validation::Error> { Err(serde_valid::validation::Error::Custom( "Rule error.".to_owned(), )) } #[derive(Validate)] + #[validate(custom(err_rule))] enum TestEnum { - #[rule(err_rule(a, b))] Named { #[validate] a: TestStruct, diff --git a/serde_valid_derive/src/derive/enum_derive.rs b/serde_valid_derive/src/derive/enum_derive.rs index bcf3480..3877a10 100644 --- a/serde_valid_derive/src/derive/enum_derive.rs +++ b/serde_valid_derive/src/derive/enum_derive.rs @@ -26,7 +26,12 @@ pub fn expand_enum_validate_derive( .into_iter() .map(|variant| match &variant.fields { syn::Fields::Named(named_fields) => { - match expand_enum_variant_named_fields_validation(ident, variant, named_fields) { + match expand_enum_variant_named_fields_validation( + ident, + input, + variant, + named_fields, + ) { Ok(variant_varidates_and_rules) => variant_varidates_and_rules, Err(variant_errors) => { errors.extend(variant_errors); @@ -66,6 +71,7 @@ pub fn expand_enum_validate_derive( fn validate(&self) -> std::result::Result<(), ::serde_valid::validation::Errors> { #validations_and_rules + Ok(()) } } @@ -79,6 +85,7 @@ pub fn expand_enum_validate_derive( fn expand_enum_variant_named_fields_validation( ident: &syn::Ident, + input: &syn::DeriveInput, variant: &syn::Variant, named_fields: &syn::FieldsNamed, ) -> Result { @@ -102,7 +109,7 @@ fn expand_enum_variant_named_fields_validation( } }; - let enum_validations = match collect_variant_custom_from_named_variant(&variant.attrs) { + let enum_validates = match collect_variant_custom_from_named_variant(&input.attrs) { Ok(validations) => TokenStream::from_iter(validations), Err(rule_errors) => { errors.extend(rule_errors); @@ -143,8 +150,8 @@ fn expand_enum_variant_named_fields_validation( let mut __rule_vec_errors = ::serde_valid::validation::VecErrors::new(); let mut __property_vec_errors_map = ::serde_valid::validation::PropertyVecErrorsMap::new(); + #enum_validates #validates - #enum_validations #rules if !(__rule_vec_errors.is_empty() && __property_vec_errors_map.is_empty()) { From 81dc210fd1811039621094df10828149b747e6f1 Mon Sep 17 00:00:00 2001 From: yassun7010 Date: Sat, 15 Jun 2024 12:42:03 +0900 Subject: [PATCH 08/10] chore: update test name. --- serde_valid/tests/enum_test.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/serde_valid/tests/enum_test.rs b/serde_valid/tests/enum_test.rs index f7dd04f..ada124d 100644 --- a/serde_valid/tests/enum_test.rs +++ b/serde_valid/tests/enum_test.rs @@ -2,12 +2,12 @@ use serde_json::json; use serde_valid::Validate; #[test] -fn enum_named_variant_validation_is_ok() { - fn ok_rule(_value: &TestEnum) -> Result<(), serde_valid::validation::Error> { +fn enum_named_enum_validation_is_ok() { + fn ok_enum(_value: &TestEnum) -> Result<(), serde_valid::validation::Error> { Ok(()) } #[derive(Validate)] - #[validate(custom(ok_rule))] + #[validate(custom(ok_enum))] enum TestEnum { Named { #[validate] @@ -70,7 +70,7 @@ fn enum_newtype_variant_validation_is_ok() { } #[test] -fn enum_named_variant_validation_is_err() { +fn enum_named_enum_validation_is_err() { fn err_rule(_data: &TestEnum) -> Result<(), serde_valid::validation::Error> { Err(serde_valid::validation::Error::Custom( "Rule error.".to_owned(), From 60b0e65f20be186737a68d00794d276ad98b856c Mon Sep 17 00:00:00 2001 From: yassun7010 Date: Sat, 15 Jun 2024 19:00:02 +0900 Subject: [PATCH 09/10] refactor: warning logic. --- Cargo.toml | 1 + serde_valid/Cargo.toml | 8 +- serde_valid_derive/Cargo.toml | 4 +- .../src/attribute/rule/named_struct_rule.rs | 8 +- .../src/attribute/rule/unnamed_struct_rule.rs | 24 ++++- serde_valid_derive/src/derive.rs | 5 +- serde_valid_derive/src/derive/enum_derive.rs | 38 ++++---- .../src/derive/named_struct_derive.rs | 46 ++++----- .../src/derive/unnamed_struct_derive.rs | 56 ++++++----- serde_valid_derive/src/lib.rs | 11 +-- serde_valid_derive/src/output_stream.rs | 3 +- serde_valid_derive/src/warning.rs | 94 ++++++++++++++++++- 12 files changed, 207 insertions(+), 91 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 9f6e1ed..79b979a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,3 +16,4 @@ regex = "^1.6" paste = "^1.0" serde_json = "^1.0" serde = "^1.0" +itertools = "0.13.0" diff --git a/serde_valid/Cargo.toml b/serde_valid/Cargo.toml index 4128b08..bfe8067 100644 --- a/serde_valid/Cargo.toml +++ b/serde_valid/Cargo.toml @@ -14,13 +14,13 @@ version.workspace = true [dependencies] fluent = { package = "fluent", version = "^0.16.0", optional = true } indexmap = { version = "^2.0", features = ["serde"] } -itertools = "^0.13" +itertools.workspace = true num-traits = "^0.2" once_cell = "^1.7" -paste = { workspace = true } -regex = { workspace = true } +paste.workspace = true +regex.workspace = true serde = { workspace = true, features = ["derive"] } -serde_json = { workspace = true } +serde_json.workspace = true serde_toml = { package = "toml", version = "^0.8", optional = true } serde_valid_derive = { path = "../serde_valid_derive" } serde_valid_literal = { path = "../serde_valid_literal" } diff --git a/serde_valid_derive/Cargo.toml b/serde_valid_derive/Cargo.toml index 7142e57..c0d7ecd 100644 --- a/serde_valid_derive/Cargo.toml +++ b/serde_valid_derive/Cargo.toml @@ -16,9 +16,9 @@ version.workspace = true proc-macro = true [dependencies] -paste = { workspace = true } +itertools.workspace = true +paste.workspace = true proc-macro-error = "^1.0" -proc-macro-warning = "1.0.2" proc-macro2 = "^1.0" quote = "^1.0" strsim = "0.11.0" diff --git a/serde_valid_derive/src/attribute/rule/named_struct_rule.rs b/serde_valid_derive/src/attribute/rule/named_struct_rule.rs index b750df7..01269a8 100644 --- a/serde_valid_derive/src/attribute/rule/named_struct_rule.rs +++ b/serde_valid_derive/src/attribute/rule/named_struct_rule.rs @@ -1,16 +1,17 @@ use std::collections::HashSet; use proc_macro2::TokenStream; -use proc_macro_warning::FormattedWarning; use quote::{quote, ToTokens}; use syn::spanned::Spanned; use crate::{ output_stream::OutputStream, types::{CommaSeparatedNestedMetas, CommaSeparatedTokenStreams}, + warning::Warning, }; pub fn collect_rules_from_named_struct( + ident: &syn::Ident, attributes: &[syn::Attribute], ) -> Result<(HashSet, OutputStream), crate::Errors> { let mut errors = vec![]; @@ -21,9 +22,8 @@ pub fn collect_rules_from_named_struct( .iter() .filter(|attribute| attribute.path().is_ident("rule")) .inspect(|attribute| { - warnings.push(FormattedWarning::new_deprecated( - "rule", - "#[rule(...)] is deprecated, use #[validate(custom(...)))] instead", + warnings.push(Warning::new_rule_deprecated( + ident, attribute.bracket_token.span.span(), )); }) diff --git a/serde_valid_derive/src/attribute/rule/unnamed_struct_rule.rs b/serde_valid_derive/src/attribute/rule/unnamed_struct_rule.rs index 80e603e..2219c18 100644 --- a/serde_valid_derive/src/attribute/rule/unnamed_struct_rule.rs +++ b/serde_valid_derive/src/attribute/rule/unnamed_struct_rule.rs @@ -4,17 +4,29 @@ use proc_macro2::TokenStream; use quote::quote; use syn::spanned::Spanned; -use crate::types::{CommaSeparatedNestedMetas, CommaSeparatedTokenStreams, NestedMeta}; +use crate::{ + output_stream::OutputStream, + types::{CommaSeparatedNestedMetas, CommaSeparatedTokenStreams, NestedMeta}, + warning::Warning, +}; pub fn collect_rules_from_unnamed_struct( + ident: &syn::Ident, attributes: &[syn::Attribute], -) -> Result<(HashSet, TokenStream), crate::Errors> { +) -> Result<(HashSet, OutputStream), crate::Errors> { let mut errors = vec![]; let mut rule_fields = HashSet::new(); + let mut warnings = vec![]; let rules = attributes .iter() .filter(|attribute| attribute.path().is_ident("rule")) + .inspect(|attribute| { + warnings.push(Warning::new_rule_deprecated( + ident, + attribute.bracket_token.span.span(), + )); + }) .filter_map(|attribute| match &attribute.meta { syn::Meta::List(list) => match collect_rule(list) { Ok((field_ident, stream)) => { @@ -36,7 +48,13 @@ pub fn collect_rules_from_unnamed_struct( .collect::>(); if errors.is_empty() { - Ok((rule_fields, TokenStream::from_iter(rules))) + Ok(( + rule_fields, + OutputStream { + output: TokenStream::from_iter(rules), + warnings, + }, + )) } else { Err(errors) } diff --git a/serde_valid_derive/src/derive.rs b/serde_valid_derive/src/derive.rs index 184672b..9067bcc 100644 --- a/serde_valid_derive/src/derive.rs +++ b/serde_valid_derive/src/derive.rs @@ -4,11 +4,10 @@ mod unnamed_struct_derive; use enum_derive::expand_enum_validate_derive; use named_struct_derive::expand_named_struct_derive; +use proc_macro2::TokenStream; use unnamed_struct_derive::expand_unnamed_struct_derive; -use crate::output_stream::OutputStream; - -pub fn expand_derive(input: &syn::DeriveInput) -> Result { +pub fn expand_derive(input: &syn::DeriveInput) -> Result { match &input.data { syn::Data::Struct(syn::DataStruct { ref fields, .. }) => match fields { syn::Fields::Named(fields) => expand_named_struct_derive(input, fields), diff --git a/serde_valid_derive/src/derive/enum_derive.rs b/serde_valid_derive/src/derive/enum_derive.rs index 3877a10..8d1df1c 100644 --- a/serde_valid_derive/src/derive/enum_derive.rs +++ b/serde_valid_derive/src/derive/enum_derive.rs @@ -10,13 +10,12 @@ use proc_macro2::TokenStream; use quote::quote; use std::collections::HashSet; use std::iter::FromIterator; - pub type Variants = syn::punctuated::Punctuated; pub fn expand_enum_validate_derive( input: &syn::DeriveInput, variants: &Variants, -) -> Result { +) -> Result { let ident = &input.ident; let (impl_generics, type_generics, where_clause) = input.generics.split_for_impl(); @@ -62,22 +61,21 @@ pub fn expand_enum_validate_derive( let warnings = validations .into_iter() .flat_map(|variant| variant.warnings) + .enumerate() + .map(|(index, warning)| warning.add_index(index)) .collect::>(); if errors.is_empty() { - Ok(OutputStream { - output: quote!( - impl #impl_generics ::serde_valid::Validate for #ident #type_generics #where_clause { - fn validate(&self) -> std::result::Result<(), ::serde_valid::validation::Errors> { - #validations_and_rules - + Ok(quote!( + impl #impl_generics ::serde_valid::Validate for #ident #type_generics #where_clause { + fn validate(&self) -> std::result::Result<(), ::serde_valid::validation::Errors> { + #( #warnings )* + #validations_and_rules - Ok(()) - } + Ok(()) } - ), - warnings, - }) + } + )) } else { Err(errors) } @@ -101,7 +99,7 @@ fn expand_enum_variant_named_fields_validation( output: rules, warnings, }, - ) = match collect_rules_from_named_struct(&variant.attrs) { + ) = match collect_rules_from_named_struct(&variant.ident, &variant.attrs) { Ok(field_rules) => field_rules, Err(variant_errors) => { errors.extend(variant_errors); @@ -176,11 +174,17 @@ fn expand_enum_variant_unnamed_fields_varidation( let variant_ident = &variant.ident; let mut fields_idents = CommaSeparatedTokenStreams::new(); - let (rule_fields, rules) = match collect_rules_from_unnamed_struct(&variant.attrs) { + let ( + rule_fields, + OutputStream { + output: rules, + warnings, + }, + ) = match collect_rules_from_unnamed_struct(&variant.ident, &variant.attrs) { Ok(field_rules) => field_rules, Err(variant_errors) => { errors.extend(variant_errors); - (HashSet::new(), quote!()) + (HashSet::new(), OutputStream::new()) } }; @@ -229,7 +233,7 @@ fn expand_enum_variant_unnamed_fields_varidation( } } ), - warnings: vec![], + warnings, }) } else { Err(errors) diff --git a/serde_valid_derive/src/derive/named_struct_derive.rs b/serde_valid_derive/src/derive/named_struct_derive.rs index a0f2b4b..ded49ce 100644 --- a/serde_valid_derive/src/derive/named_struct_derive.rs +++ b/serde_valid_derive/src/derive/named_struct_derive.rs @@ -14,7 +14,7 @@ use std::iter::FromIterator; pub fn expand_named_struct_derive( input: &syn::DeriveInput, fields: &syn::FieldsNamed, -) -> Result { +) -> Result { let ident = &input.ident; let (impl_generics, type_generics, where_clause) = input.generics.split_for_impl(); let rename_map = collect_serde_rename_map(fields); @@ -27,7 +27,7 @@ pub fn expand_named_struct_derive( output: rules, warnings, }, - ) = match collect_rules_from_named_struct(&input.attrs) { + ) = match collect_rules_from_named_struct(&input.ident, &input.attrs) { Ok((rule_fields, rules)) => (rule_fields, rules), Err(rule_errors) => { errors.extend(rule_errors); @@ -58,28 +58,32 @@ pub fn expand_named_struct_derive( let fields_errors = object_errors_tokens(); + let warnings = warnings + .into_iter() + .enumerate() + .map(|(index, warning)| warning.add_index(index)) + .collect::>(); + if errors.is_empty() { - Ok(OutputStream { - output: quote!( - impl #impl_generics ::serde_valid::Validate for #ident #type_generics #where_clause { - fn validate(&self) -> std::result::Result<(), ::serde_valid::validation::Errors> { - let mut __rule_vec_errors = ::serde_valid::validation::VecErrors::new(); - let mut __property_vec_errors_map = ::serde_valid::validation::PropertyVecErrorsMap::new(); - - #field_validates - #struct_validations - #rules - - if __rule_vec_errors.is_empty() && __property_vec_errors_map.is_empty() { - Ok(()) - } else { - Err(#fields_errors) - } + Ok(quote!( + impl #impl_generics ::serde_valid::Validate for #ident #type_generics #where_clause { + fn validate(&self) -> std::result::Result<(), ::serde_valid::validation::Errors> { + #(#warnings)* + let mut __rule_vec_errors = ::serde_valid::validation::VecErrors::new(); + let mut __property_vec_errors_map = ::serde_valid::validation::PropertyVecErrorsMap::new(); + + #field_validates + #struct_validations + #rules + + if __rule_vec_errors.is_empty() && __property_vec_errors_map.is_empty() { + Ok(()) + } else { + Err(#fields_errors) } } - ), - warnings, - }) + } + )) } else { Err(errors) } diff --git a/serde_valid_derive/src/derive/unnamed_struct_derive.rs b/serde_valid_derive/src/derive/unnamed_struct_derive.rs index 910e7a3..f0e0bc6 100644 --- a/serde_valid_derive/src/derive/unnamed_struct_derive.rs +++ b/serde_valid_derive/src/derive/unnamed_struct_derive.rs @@ -13,17 +13,23 @@ use std::iter::FromIterator; pub fn expand_unnamed_struct_derive( input: &syn::DeriveInput, fields: &syn::FieldsUnnamed, -) -> Result { +) -> Result { let ident = &input.ident; let (impl_generics, type_generics, where_clause) = input.generics.split_for_impl(); let mut errors = vec![]; - let (rule_fields, rules) = match collect_rules_from_unnamed_struct(&input.attrs) { - Ok((rule_fields, rules)) => (rule_fields, TokenStream::from_iter(rules)), + let ( + rule_fields, + OutputStream { + output: rules, + warnings, + }, + ) = match collect_rules_from_unnamed_struct(&input.ident, &input.attrs) { + Ok((rule_fields, rules)) => (rule_fields, rules), Err(rule_errors) => { errors.extend(rule_errors); - (HashSet::new(), quote!()) + (HashSet::new(), OutputStream::new()) } }; @@ -55,28 +61,32 @@ pub fn expand_unnamed_struct_derive( new_type_errors_tokens() }; + let warnings = warnings + .into_iter() + .enumerate() + .map(|(index, warning)| warning.add_index(index)) + .collect::>(); + if errors.is_empty() { - Ok(OutputStream { - output: quote!( - impl #impl_generics ::serde_valid::Validate for #ident #type_generics #where_clause { - fn validate(&self) -> std::result::Result<(), ::serde_valid::validation::Errors> { - let mut __rule_vec_errors = ::serde_valid::validation::VecErrors::new(); - let mut __item_vec_errors_map = ::serde_valid::validation::ItemVecErrorsMap::new(); - - #field_validates - #struct_validations - #rules - - if __rule_vec_errors.is_empty() && __item_vec_errors_map.is_empty() { - Ok(()) - } else { - Err(#fields_errors) - } + Ok(quote!( + #(#warnings)* + impl #impl_generics ::serde_valid::Validate for #ident #type_generics #where_clause { + fn validate(&self) -> std::result::Result<(), ::serde_valid::validation::Errors> { + let mut __rule_vec_errors = ::serde_valid::validation::VecErrors::new(); + let mut __item_vec_errors_map = ::serde_valid::validation::ItemVecErrorsMap::new(); + + #field_validates + #struct_validations + #rules + + if __rule_vec_errors.is_empty() && __item_vec_errors_map.is_empty() { + Ok(()) + } else { + Err(#fields_errors) } } - ), - warnings: vec![], - }) + } + )) } else { Err(errors) } diff --git a/serde_valid_derive/src/lib.rs b/serde_valid_derive/src/lib.rs index a29059a..69c1180 100644 --- a/serde_valid_derive/src/lib.rs +++ b/serde_valid_derive/src/lib.rs @@ -10,10 +10,8 @@ mod warning; use derive::expand_derive; use error::to_compile_errors; use error::{Error, Errors}; -use output_stream::OutputStream; use proc_macro::TokenStream; use proc_macro_error::proc_macro_error; -use quote::quote; use syn::{parse_macro_input, DeriveInput}; #[proc_macro_derive(Validate, attributes(rule, validate, serde_valid))] @@ -22,13 +20,6 @@ pub fn derive_validate(tokens: TokenStream) -> TokenStream { let input = parse_macro_input!(tokens as DeriveInput); expand_derive(&input) - .map_or_else(to_compile_errors, |OutputStream { output, warnings }| { - quote!( - #output - #( - #warnings - )* - ) - }) + .unwrap_or_else(to_compile_errors) .into() } diff --git a/serde_valid_derive/src/output_stream.rs b/serde_valid_derive/src/output_stream.rs index 20dd41d..478c23c 100644 --- a/serde_valid_derive/src/output_stream.rs +++ b/serde_valid_derive/src/output_stream.rs @@ -1,6 +1,5 @@ -use proc_macro2::TokenStream; - use crate::warning::Warning; +use proc_macro2::TokenStream; #[derive(Debug, Clone)] pub struct OutputStream { diff --git a/serde_valid_derive/src/warning.rs b/serde_valid_derive/src/warning.rs index e4c239f..e1e5a06 100644 --- a/serde_valid_derive/src/warning.rs +++ b/serde_valid_derive/src/warning.rs @@ -1,3 +1,93 @@ -use proc_macro_warning::FormattedWarning; +use std::{hash::Hash, str::FromStr}; -pub type Warning = FormattedWarning; +use proc_macro2::Span; +use proc_macro2::TokenStream; +use quote::{quote_spanned, ToTokens}; + +#[derive(Debug, Clone)] +pub enum Warning { + Deprecated { + ident: syn::Ident, + note: String, + span: Span, + }, +} + +impl Hash for Warning { + fn hash(&self, state: &mut H) { + match self { + Self::Deprecated { ident, note, .. } => { + ident.hash(state); + note.hash(state); + } + } + } +} + +impl std::cmp::PartialEq for Warning { + fn eq(&self, other: &Self) -> bool { + match (self, other) { + ( + Self::Deprecated { + ident: ident1, + note: note1, + .. + }, + Self::Deprecated { + ident: ident2, + note: note2, + .. + }, + ) => ident1 == ident2 && note1 == note2, + } + } +} + +impl std::cmp::Eq for Warning {} + +impl Warning { + pub fn new_rule_deprecated(ident: &syn::Ident, span: Span) -> Self { + Self::Deprecated { + ident: ident.clone(), + note: ident.to_string() + + ": #[rule(...)] is deprecated, use #[validate(custom(...)))] instead", + span, + } + } + + pub fn add_index(&self, index: usize) -> Self { + match self { + Self::Deprecated { ident, note, span } => Self::Deprecated { + ident: syn::Ident::new(&format!("{}_{}", ident, index), ident.span()), + note: note.clone(), + span: *span, + }, + } + } +} + +impl ToTokens for Warning { + fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) { + match self { + Self::Deprecated { ident, note, span } => { + let func_name = TokenStream::from_str(&format!( + "__{}_warning", + ident.to_string().to_lowercase() + )) + .unwrap(); + + quote_spanned!(*span => + #[deprecated(note = #note)] + #[allow(clippy::let_unit_value)] + fn #func_name() { + #[deprecated(note = #note)] + #[allow(non_upper_case_globals)] + const _deprecated: () = (); + let _ = _deprecated; + } + ) + .to_tokens(tokens) + } + } + } +} From 574b49e41dc38f804b33e6dc59d9f119e8c42b89 Mon Sep 17 00:00:00 2001 From: yassun7010 Date: Sat, 15 Jun 2024 19:05:21 +0900 Subject: [PATCH 10/10] chore: change message. --- serde_valid_derive/src/warning.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/serde_valid_derive/src/warning.rs b/serde_valid_derive/src/warning.rs index e1e5a06..cb22d3d 100644 --- a/serde_valid_derive/src/warning.rs +++ b/serde_valid_derive/src/warning.rs @@ -49,8 +49,7 @@ impl Warning { pub fn new_rule_deprecated(ident: &syn::Ident, span: Span) -> Self { Self::Deprecated { ident: ident.clone(), - note: ident.to_string() - + ": #[rule(...)] is deprecated, use #[validate(custom(...)))] instead", + note: "#[rule(...)] is deprecated, use #[validate(custom(...)))] instead".to_string(), span, } }