diff --git a/CHANGELOG.md b/CHANGELOG.md index db8194e..d43a0af 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +### v0.x.x - 202x-xx-xx + +* **[FIX]** Enable to specify custom error as a path (see [#186](https://github.com/greyblake/nutype/issues/186), [#187](https://github.com/greyblake/nutype/pull/187)) + ### v0.5.0 - 2024-xx-xx - **[FEATURE]** Added support for custom error types and validation functions via the `error` and `with` attributes. diff --git a/nutype_macros/src/any/gen/error.rs b/nutype_macros/src/any/gen/error.rs index f275218..383543f 100644 --- a/nutype_macros/src/any/gen/error.rs +++ b/nutype_macros/src/any/gen/error.rs @@ -5,18 +5,18 @@ use crate::{ any::models::AnyValidator, common::{ gen::error::gen_impl_error_trait, - models::{ErrorTypeName, TypeName}, + models::{ErrorTypePath, TypeName}, }, }; pub fn gen_validation_error_type( type_name: &TypeName, - error_type_name: &ErrorTypeName, + error_type_path: &ErrorTypePath, validators: &[AnyValidator], ) -> TokenStream { - let definition = gen_definition(error_type_name, validators); - let impl_display_trait = gen_impl_display_trait(type_name, error_type_name, validators); - let impl_error_trait = gen_impl_error_trait(error_type_name); + let definition = gen_definition(error_type_path, validators); + let impl_display_trait = gen_impl_display_trait(type_name, error_type_path, validators); + let impl_error_trait = gen_impl_error_trait(error_type_path); quote! { #[derive(Debug, Clone, PartialEq, Eq)] @@ -27,7 +27,7 @@ pub fn gen_validation_error_type( } } -fn gen_definition(error_type_name: &ErrorTypeName, validators: &[AnyValidator]) -> TokenStream { +fn gen_definition(error_type_path: &ErrorTypePath, validators: &[AnyValidator]) -> TokenStream { let error_variants: TokenStream = validators .iter() .map(|validator| match validator { @@ -39,7 +39,7 @@ fn gen_definition(error_type_name: &ErrorTypeName, validators: &[AnyValidator]) quote! { #[allow(clippy::enum_variant_names)] - pub enum #error_type_name { + pub enum #error_type_path { #error_variants } } @@ -47,17 +47,17 @@ fn gen_definition(error_type_name: &ErrorTypeName, validators: &[AnyValidator]) fn gen_impl_display_trait( type_name: &TypeName, - error_type_name: &ErrorTypeName, + error_type_path: &ErrorTypePath, validators: &[AnyValidator], ) -> TokenStream { let match_arms = validators.iter().map(|validator| match validator { AnyValidator::Predicate(_) => quote! { - #error_type_name::PredicateViolated => write!(f, "{} failed the predicate test.", stringify!(#type_name)) + #error_type_path::PredicateViolated => write!(f, "{} failed the predicate test.", stringify!(#type_name)) }, }); quote! { - impl ::core::fmt::Display for #error_type_name { + impl ::core::fmt::Display for #error_type_path { fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result { match self { #(#match_arms,)* diff --git a/nutype_macros/src/any/gen/mod.rs b/nutype_macros/src/any/gen/mod.rs index 33afb7b..0603136 100644 --- a/nutype_macros/src/any/gen/mod.rs +++ b/nutype_macros/src/any/gen/mod.rs @@ -11,7 +11,7 @@ use crate::common::{ gen::{ tests::gen_test_should_have_valid_default_value, traits::GeneratedTraits, GenerateNewtype, }, - models::{ErrorTypeName, Guard, TypeName, TypedCustomFunction}, + models::{ErrorTypePath, Guard, TypeName, TypedCustomFunction}, }; use self::error::gen_validation_error_type; @@ -61,7 +61,7 @@ impl GenerateNewtype for AnyNewtype { fn gen_fn_validate( inner_type: &Self::InnerType, - error_type_name: &ErrorTypeName, + error_type_path: &ErrorTypePath, validators: &[Self::Validator], ) -> TokenStream { let validations: TokenStream = validators @@ -77,7 +77,7 @@ impl GenerateNewtype for AnyNewtype { .expect("Failed to convert predicate into a typed closure"); quote!( if !(#typed_predicate)(val) { - return Err(#error_type_name::PredicateViolated); + return Err(#error_type_path::PredicateViolated); } ) } @@ -98,7 +98,7 @@ impl GenerateNewtype for AnyNewtype { // Since this code is generic which is used for different inner types (not only Cow), we cannot easily fix it to make // clippy happy. #[allow(clippy::ptr_arg)] - fn __validate__<'nutype_a>(val: &'nutype_a #inner_type) -> ::core::result::Result<(), #error_type_name> { + fn __validate__<'nutype_a>(val: &'nutype_a #inner_type) -> ::core::result::Result<(), #error_type_path> { #validations Ok(()) } @@ -107,10 +107,10 @@ impl GenerateNewtype for AnyNewtype { fn gen_validation_error_type( type_name: &TypeName, - error_type_name: &ErrorTypeName, + error_type_path: &ErrorTypePath, validators: &[Self::Validator], ) -> TokenStream { - gen_validation_error_type(type_name, error_type_name, validators) + gen_validation_error_type(type_name, error_type_path, validators) } fn gen_traits( diff --git a/nutype_macros/src/any/gen/traits/mod.rs b/nutype_macros/src/any/gen/traits/mod.rs index 3798910..8b53c8e 100644 --- a/nutype_macros/src/any/gen/traits/mod.rs +++ b/nutype_macros/src/any/gen/traits/mod.rs @@ -147,7 +147,7 @@ fn gen_implemented_traits( maybe_default_value: Option, guard: &AnyGuard, ) -> Result { - let maybe_error_type_name = guard.maybe_error_type_name(); + let maybe_error_type_name = guard.maybe_error_type_path(); impl_traits .iter() .map(|t| match t { diff --git a/nutype_macros/src/common/gen/error.rs b/nutype_macros/src/common/gen/error.rs index 43ecf3a..64fb8e7 100644 --- a/nutype_macros/src/common/gen/error.rs +++ b/nutype_macros/src/common/gen/error.rs @@ -2,23 +2,23 @@ use cfg_if::cfg_if; use proc_macro2::TokenStream; use quote::{format_ident, quote}; -use crate::common::models::{ErrorTypeName, TypeName}; +use crate::common::models::{ErrorTypePath, TypeName}; /// Generate a default error type name if the error name is not specified explicitly by /// the user in the attributes. -pub fn gen_error_type_name(type_name: &TypeName) -> ErrorTypeName { +pub fn gen_error_type_name(type_name: &TypeName) -> ErrorTypePath { let ident = format_ident!("{type_name}Error"); - ErrorTypeName::new(ident) + ErrorTypePath::new(ident) } // NOTE: There is no `::core::error::Error` yet in stable Rust. // So for `no_std` we just don't implement `Error` trait. #[allow(unused_variables)] -pub fn gen_impl_error_trait(error_type_name: &ErrorTypeName) -> TokenStream { +pub fn gen_impl_error_trait(error_type_path: &ErrorTypePath) -> TokenStream { cfg_if! { if #[cfg(feature = "std")] { quote! { - impl ::std::error::Error for #error_type_name { + impl ::std::error::Error for #error_type_path { fn source(&self) -> Option<&(dyn ::std::error::Error + 'static)> { None } diff --git a/nutype_macros/src/common/gen/mod.rs b/nutype_macros/src/common/gen/mod.rs index 01da127..650797c 100644 --- a/nutype_macros/src/common/gen/mod.rs +++ b/nutype_macros/src/common/gen/mod.rs @@ -9,7 +9,7 @@ use std::{collections::HashSet, hash::Hash}; use self::traits::GeneratedTraits; use super::models::{ - CustomFunction, ErrorTypeName, GenerateParams, Guard, NewUnchecked, ParseErrorTypeName, + CustomFunction, ErrorTypePath, GenerateParams, Guard, NewUnchecked, ParseErrorTypeName, TypeName, TypeTrait, }; use crate::common::{ @@ -99,27 +99,27 @@ pub fn gen_reimports( vis: Visibility, type_name: &TypeName, module_name: &ModuleName, - maybe_error_type_name: Option<&ErrorTypeName>, + maybe_error_type_path: Option<&ErrorTypePath>, maybe_parse_error_type_name: Option<&ParseErrorTypeName>, ) -> TokenStream { let reimport_main_type = quote! { #vis use #module_name::#type_name; }; - let reimport_error_type_if_needed = match maybe_error_type_name { + let reimport_error_type_if_needed = match maybe_error_type_path { None => quote!(), - Some(ref error_type_name) => { + Some(ref error_type_path) => { quote! ( - #vis use #module_name::#error_type_name; + #vis use #module_name::#error_type_path; ) } }; let reimport_parse_error_type_if_needed = match maybe_parse_error_type_name { None => quote!(), - Some(ref parse_error_type_name) => { + Some(ref parse_error_type_path) => { quote! ( - #vis use #module_name::#parse_error_type_name; + #vis use #module_name::#parse_error_type_path; ) } }; @@ -224,13 +224,13 @@ pub trait GenerateNewtype { fn gen_fn_validate( inner_type: &Self::InnerType, - error_type_name: &ErrorTypeName, + error_type_path: &ErrorTypePath, validators: &[Self::Validator], ) -> TokenStream; fn gen_validation_error_type( type_name: &TypeName, - error_type_name: &ErrorTypeName, + error_type_path: &ErrorTypePath, validators: &[Self::Validator], ) -> TokenStream; @@ -256,10 +256,10 @@ pub trait GenerateNewtype { let maybe_generated_validation_error = match validation { Validation::Standard { validators, - error_type_name, + error_type_path, } => { let validation_error = - Self::gen_validation_error_type(type_name, error_type_name, validators); + Self::gen_validation_error_type(type_name, error_type_path, validators); Some(validation_error) } Validation::Custom { .. } => None, @@ -268,12 +268,12 @@ pub trait GenerateNewtype { let fn_validate = match validation { Validation::Standard { validators, - error_type_name, - } => Self::gen_fn_validate(inner_type, error_type_name, validators), + error_type_path, + } => Self::gen_fn_validate(inner_type, error_type_path, validators), Validation::Custom { with, - error_type_name, - } => gen_fn_validate_custom(inner_type, with, error_type_name), + error_type_path, + } => gen_fn_validate_custom(inner_type, with, error_type_path), }; let (input_type, convert_raw_value_if_necessary) = if Self::NEW_CONVERT_INTO_INNER_TYPE { @@ -285,13 +285,13 @@ pub trait GenerateNewtype { (quote!(#inner_type), quote!()) }; - let error_type_name = validation.error_type_name(); + let error_type_path = validation.error_type_path(); quote!( #maybe_generated_validation_error impl #generics #type_name #generics_without_bounds { - pub fn try_new(raw_value: #input_type) -> ::core::result::Result { + pub fn try_new(raw_value: #input_type) -> ::core::result::Result { #convert_raw_value_if_necessary let sanitized_value: #inner_type = Self::__sanitize__(raw_value); @@ -306,7 +306,7 @@ pub trait GenerateNewtype { // TODO: Remove in 0.5.0 #[deprecated(since="0.4.3", note="\nUse `try_new` instead.")] - pub fn new(raw_value: #input_type) -> ::core::result::Result { + pub fn new(raw_value: #input_type) -> ::core::result::Result { Self::try_new(raw_value) } } @@ -395,7 +395,7 @@ pub trait GenerateNewtype { Self::gen_implementation(&type_name, &generics, &inner_type, &guard, new_unchecked); let has_from_str_trait = traits.iter().any(|t| t.is_from_str()); - let maybe_parse_error_type_name = if has_from_str_trait && Self::HAS_DEDICATED_PARSE_ERROR { + let maybe_parse_error_type_path = if has_from_str_trait && Self::HAS_DEDICATED_PARSE_ERROR { Some(gen_parse_error_name(&type_name)) } else { None @@ -410,14 +410,14 @@ pub trait GenerateNewtype { &traits, ); - let maybe_reimported_error_type_name = match &guard { + let maybe_reimported_error_type_path = match &guard { Guard::WithoutValidation { .. } => None, Guard::WithValidation { validation, .. } => match validation { // We won't need to reimport error if it's a custom error provided by the user. Validation::Custom { .. } => None, Validation::Standard { - error_type_name, .. - } => Some(error_type_name), + error_type_path, .. + } => Some(error_type_path), }, }; @@ -425,8 +425,8 @@ pub trait GenerateNewtype { vis, &type_name, &module_name, - maybe_reimported_error_type_name, - maybe_parse_error_type_name.as_ref(), + maybe_reimported_error_type_path, + maybe_parse_error_type_path.as_ref(), ); let GeneratedTraits { @@ -472,13 +472,13 @@ pub trait GenerateNewtype { fn gen_fn_validate_custom( inner_type: &InnerType, with: &CustomFunction, - error_type_name: &ErrorTypeName, + error_type_path: &ErrorTypePath, ) -> TokenStream { quote! { // For some types like `String` clippy suggests using `&str` instead of `&String` here, // but it does not really matter in this context. #[allow(clippy::ptr_arg)] - fn __validate__(value: &#inner_type) -> ::core::result::Result<(), #error_type_name> { + fn __validate__(value: &#inner_type) -> ::core::result::Result<(), #error_type_path> { #with(value) } } diff --git a/nutype_macros/src/common/gen/parse_error.rs b/nutype_macros/src/common/gen/parse_error.rs index 07a4db6..2aa8e87 100644 --- a/nutype_macros/src/common/gen/parse_error.rs +++ b/nutype_macros/src/common/gen/parse_error.rs @@ -5,7 +5,7 @@ use syn::Generics; use crate::common::{ gen::{add_bound_to_all_type_params, strip_trait_bounds_on_generics}, - models::{ErrorTypeName, InnerType, ParseErrorTypeName, TypeName}, + models::{ErrorTypePath, InnerType, ParseErrorTypeName, TypeName}, }; /// Generate a name for the error which is used for FromStr trait implementation. @@ -20,7 +20,7 @@ pub fn gen_def_parse_error( type_name: &TypeName, generics: &Generics, inner_type: impl Into, - maybe_error_type_name: Option<&ErrorTypeName>, + maybe_error_type_name: Option<&ErrorTypePath>, parse_error_type_name: &ParseErrorTypeName, ) -> TokenStream { let inner_type: InnerType = inner_type.into(); diff --git a/nutype_macros/src/common/gen/traits.rs b/nutype_macros/src/common/gen/traits.rs index 1f98ba0..f1d707e 100644 --- a/nutype_macros/src/common/gen/traits.rs +++ b/nutype_macros/src/common/gen/traits.rs @@ -6,7 +6,7 @@ use syn::Generics; use crate::common::{ gen::{add_bound_to_all_type_params, strip_trait_bounds_on_generics}, - models::{ErrorTypeName, InnerType, TypeName}, + models::{ErrorTypePath, InnerType, TypeName}, }; use super::parse_error::{gen_def_parse_error, gen_parse_error_name}; @@ -172,7 +172,7 @@ pub fn gen_impl_trait_try_from( type_name: &TypeName, generics: &Generics, inner_type: impl ToTokens, - maybe_error_type_name: Option<&ErrorTypeName>, + maybe_error_type_name: Option<&ErrorTypePath>, ) -> TokenStream { let generics_without_bounds = strip_trait_bounds_on_generics(generics); @@ -213,7 +213,7 @@ pub fn gen_impl_trait_from_str( type_name: &TypeName, generics: &Generics, inner_type: impl Into, - maybe_error_type_name: Option<&ErrorTypeName>, + maybe_error_type_name: Option<&ErrorTypePath>, ) -> TokenStream { let inner_type: InnerType = inner_type.into(); let parse_error_type_name = gen_parse_error_name(type_name); @@ -286,7 +286,7 @@ pub fn gen_impl_trait_serde_deserialize( type_name: &TypeName, type_generics: &Generics, inner_type: impl Into, - maybe_error_type_name: Option<&ErrorTypeName>, + maybe_error_type_name: Option<&ErrorTypePath>, ) -> TokenStream { let inner_type: InnerType = inner_type.into(); let raw_value_to_result: TokenStream = if maybe_error_type_name.is_some() { diff --git a/nutype_macros/src/common/models.rs b/nutype_macros/src/common/models.rs index de57cd9..80d3e17 100644 --- a/nutype_macros/src/common/models.rs +++ b/nutype_macros/src/common/models.rs @@ -1,3 +1,5 @@ +mod error_type_path; + use kinded::Kinded; use std::ops::Add; use std::{collections::HashSet, fmt::Debug}; @@ -18,6 +20,8 @@ use crate::{ use super::gen::type_custom_closure; use super::parse::RawValidation; +pub use error_type_path::ErrorTypePath; + /// A spanned item. An item can be anything that cares a domain value. /// Keeping a span allows to throw good precise error messages at the validation stage. #[derive(Debug, Clone)] @@ -143,23 +147,6 @@ macro_rules! define_ident_type { // For example: `Username`, `Email`, etc. define_ident_type!(TypeName); -// Represents a type for a validation error. -// For example, if `TypeName` is `Email`, then `ErrorTypeName` would usually be `EmailError`. -define_ident_type!(ErrorTypeName); - -impl ErrorTypeName { - pub fn span(&self) -> Span { - self.0.span() - } -} - -impl Parse for ErrorTypeName { - fn parse(input: ParseStream) -> syn::Result { - let ident = input.parse::()?; - Ok(Self::new(ident)) - } -} - // A type that represents an error name which is returned by `FromStr` traits. // For example, if `TypeName` is `Amount`, then this would be `AmountParseError`. define_ident_type!(ParseErrorTypeName); @@ -230,35 +217,35 @@ pub enum Validation { /// Name of the error type. Since the type is defined by user, the macro must not generate /// it. - error_type_name: ErrorTypeName, + error_type_path: ErrorTypePath, }, Standard { /// List of the standard validators validators: Vec, /// Name of the error type. The #[nutype] macro must generate definition of this type. - error_type_name: ErrorTypeName, + error_type_path: ErrorTypePath, }, } impl Validation { - pub fn error_type_name(&self) -> &ErrorTypeName { + pub fn error_type_path(&self) -> &ErrorTypePath { match self { Self::Custom { - error_type_name, .. - } => error_type_name, + error_type_path, .. + } => error_type_path, Self::Standard { - error_type_name, .. - } => error_type_name, + error_type_path, .. + } => error_type_path, } } } impl Guard { - pub fn maybe_error_type_name(&self) -> Option<&ErrorTypeName> { + pub fn maybe_error_type_path(&self) -> Option<&ErrorTypePath> { match self { Self::WithoutValidation { .. } => None, - Self::WithValidation { validation, .. } => Some(validation.error_type_name()), + Self::WithValidation { validation, .. } => Some(validation.error_type_path()), } } } diff --git a/nutype_macros/src/common/models/error_type_path.rs b/nutype_macros/src/common/models/error_type_path.rs new file mode 100644 index 0000000..0bc2b75 --- /dev/null +++ b/nutype_macros/src/common/models/error_type_path.rs @@ -0,0 +1,41 @@ +use proc_macro2::{Span, TokenStream}; +use quote::ToTokens; +use syn::{ + parse::{Parse, ParseStream}, + spanned::Spanned, +}; + +// Represents a path to an error type. +// Could be a single Ident (e.g. `NameError`, but could also be a path (e.g. `std::io::Error`). +#[derive(Debug, Clone)] +pub struct ErrorTypePath(syn::Path); + +impl ErrorTypePath { + pub fn new(name: impl Into) -> Self { + Self(name.into()) + } + + pub fn span(&self) -> Span { + self.0.span() + } +} + +impl Parse for ErrorTypePath { + fn parse(input: ParseStream) -> syn::Result { + let path = input.parse::()?; + Ok(Self::new(path)) + } +} + +impl core::fmt::Display for ErrorTypePath { + fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { + let token_stream: TokenStream = self.0.clone().to_token_stream(); + write!(f, "{}", token_stream) + } +} + +impl ::quote::ToTokens for ErrorTypePath { + fn to_tokens(&self, token_stream: &mut TokenStream) { + self.0.to_tokens(token_stream) + } +} diff --git a/nutype_macros/src/common/parse/mod.rs b/nutype_macros/src/common/parse/mod.rs index f207e57..5b1fdaf 100644 --- a/nutype_macros/src/common/parse/mod.rs +++ b/nutype_macros/src/common/parse/mod.rs @@ -21,7 +21,7 @@ use syn::{ use crate::common::models::SpannedDeriveTrait; use super::models::{ - CustomFunction, ErrorTypeName, NewUnchecked, TypedCustomFunction, ValueOrExpr, + CustomFunction, ErrorTypePath, NewUnchecked, TypedCustomFunction, ValueOrExpr, }; pub fn is_doc_attribute(attribute: &syn::Attribute) -> bool { @@ -83,7 +83,7 @@ enum ValidateAttr { #[derive(Debug, Kinded)] #[kinded(display = "snake_case")] enum ExtraValidateAttr { - Error(ErrorTypeName), + Error(ErrorTypePath), With(CustomFunction), } @@ -93,7 +93,7 @@ impl Parse for ExtraValidateAttr { match kind { ExtraValidateAttrKind::Error => { let _eq: Token![=] = input.parse()?; - let error: ErrorTypeName = input.parse()?; + let error: ErrorTypePath = input.parse()?; Ok(ExtraValidateAttr::Error(error)) } ExtraValidateAttrKind::With => { @@ -148,7 +148,7 @@ where pub enum RawValidation { Custom { with: CustomFunction, - error: ErrorTypeName, + error: ErrorTypePath, }, Standard { validators: Vec, @@ -166,7 +166,7 @@ where let mut validators: Vec = Vec::new(); let mut maybe_with: Option = None; - let mut maybe_error: Option = None; + let mut maybe_error: Option = None; for attr in attrs { match attr { diff --git a/nutype_macros/src/common/validate.rs b/nutype_macros/src/common/validate.rs index 696efca..6739d97 100644 --- a/nutype_macros/src/common/validate.rs +++ b/nutype_macros/src/common/validate.rs @@ -30,18 +30,18 @@ pub fn validate_guard( let validation = match raw_validation { RawValidation::Standard { validators } => { - let error_type_name = gen_error_type_name(type_name); + let error_type_path = gen_error_type_name(type_name); let validators = validate_validators(validators)?; Validation::Standard { validators, - error_type_name, + error_type_path, } } RawValidation::Custom { with, error } => { - let error_type_name = error; + let error_type_path = error; Validation::Custom { with, - error_type_name, + error_type_path, } } }; diff --git a/nutype_macros/src/float/gen/error.rs b/nutype_macros/src/float/gen/error.rs index 2f93108..6d3e7ad 100644 --- a/nutype_macros/src/float/gen/error.rs +++ b/nutype_macros/src/float/gen/error.rs @@ -3,19 +3,19 @@ use quote::{quote, ToTokens}; use crate::common::{ gen::error::gen_impl_error_trait, - models::{ErrorTypeName, TypeName}, + models::{ErrorTypePath, TypeName}, }; use super::super::models::FloatValidator; pub fn gen_validation_error_type( type_name: &TypeName, - error_type_name: &ErrorTypeName, + error_type_path: &ErrorTypePath, validators: &[FloatValidator], ) -> TokenStream { - let definition = gen_definition(error_type_name, validators); - let impl_display_trait = gen_impl_display_trait(type_name, error_type_name, validators); - let impl_error_trait = gen_impl_error_trait(error_type_name); + let definition = gen_definition(error_type_path, validators); + let impl_display_trait = gen_impl_display_trait(type_name, error_type_path, validators); + let impl_error_trait = gen_impl_error_trait(error_type_path); quote! { #[derive(Debug, Clone, PartialEq, Eq)] @@ -27,7 +27,7 @@ pub fn gen_validation_error_type( } fn gen_definition( - error_type_name: &ErrorTypeName, + error_type_path: &ErrorTypePath, validators: &[FloatValidator], ) -> TokenStream { let error_variants: TokenStream = validators @@ -56,7 +56,7 @@ fn gen_definition( quote! { #[allow(clippy::enum_variant_names)] - pub enum #error_type_name { + pub enum #error_type_path { #error_variants } } @@ -64,32 +64,32 @@ fn gen_definition( fn gen_impl_display_trait( type_name: &TypeName, - error_type_name: &ErrorTypeName, + error_type_path: &ErrorTypePath, validators: &[FloatValidator], ) -> TokenStream { let match_arms = validators.iter().map(|validator| match validator { FloatValidator::Greater(val) => quote! { - #error_type_name::GreaterViolated => write!(f, "{} is too small. The value must be greater than {:#?}.", stringify!(#type_name), #val) + #error_type_path::GreaterViolated => write!(f, "{} is too small. The value must be greater than {:#?}.", stringify!(#type_name), #val) }, FloatValidator::GreaterOrEqual(val) => quote! { - #error_type_name::GreaterOrEqualViolated => write!(f, "{} is too small. The value must be greater or equal to {:#?}.", stringify!(#type_name), #val) + #error_type_path::GreaterOrEqualViolated => write!(f, "{} is too small. The value must be greater or equal to {:#?}.", stringify!(#type_name), #val) }, FloatValidator::LessOrEqual(val) => quote! { - #error_type_name::LessOrEqualViolated=> write!(f, "{} is too big. The value must be less than {:#?}.", stringify!(#type_name), #val) + #error_type_path::LessOrEqualViolated=> write!(f, "{} is too big. The value must be less than {:#?}.", stringify!(#type_name), #val) }, FloatValidator::Less(val) => quote! { - #error_type_name::LessViolated=> write!(f, "{} is too big. The value must be less or equal to {:#?}.", stringify!(#type_name), #val) + #error_type_path::LessViolated=> write!(f, "{} is too big. The value must be less or equal to {:#?}.", stringify!(#type_name), #val) }, FloatValidator::Predicate(_) => quote! { - #error_type_name::PredicateViolated => write!(f, "{} failed the predicate test.", stringify!(#type_name)) + #error_type_path::PredicateViolated => write!(f, "{} failed the predicate test.", stringify!(#type_name)) }, FloatValidator::Finite => quote! { - #error_type_name::FiniteViolated => write!(f, "{} is not finite.", stringify!(#type_name)) + #error_type_path::FiniteViolated => write!(f, "{} is not finite.", stringify!(#type_name)) }, }); quote! { - impl ::core::fmt::Display for #error_type_name { + impl ::core::fmt::Display for #error_type_path { fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result { match self { #(#match_arms,)* diff --git a/nutype_macros/src/float/gen/mod.rs b/nutype_macros/src/float/gen/mod.rs index 993ef91..c8ba003 100644 --- a/nutype_macros/src/float/gen/mod.rs +++ b/nutype_macros/src/float/gen/mod.rs @@ -22,7 +22,7 @@ use crate::{ traits::GeneratedTraits, GenerateNewtype, }, - models::{ErrorTypeName, Guard, TypeName}, + models::{ErrorTypePath, Guard, TypeName}, }, float::models::FloatInnerType, }; @@ -65,7 +65,7 @@ where fn gen_fn_validate( inner_type: &Self::InnerType, - error_type_name: &ErrorTypeName, + error_type_path: &ErrorTypePath, validators: &[Self::Validator], ) -> TokenStream { let validations: TokenStream = validators @@ -74,42 +74,42 @@ where FloatValidator::Less(exclusive_upper_bound) => { quote!( if val >= #exclusive_upper_bound { - return Err(#error_type_name::LessViolated); + return Err(#error_type_path::LessViolated); } ) } FloatValidator::LessOrEqual(max) => { quote!( if val > #max { - return Err(#error_type_name::LessOrEqualViolated); + return Err(#error_type_path::LessOrEqualViolated); } ) } FloatValidator::Greater(exclusive_lower_bound) => { quote!( if val <= #exclusive_lower_bound { - return Err(#error_type_name::GreaterViolated); + return Err(#error_type_path::GreaterViolated); } ) } FloatValidator::GreaterOrEqual(min) => { quote!( if val < #min { - return Err(#error_type_name::GreaterOrEqualViolated); + return Err(#error_type_path::GreaterOrEqualViolated); } ) } FloatValidator::Predicate(custom_is_valid_fn) => { quote!( if !(#custom_is_valid_fn)(&val) { - return Err(#error_type_name::PredicateViolated); + return Err(#error_type_path::PredicateViolated); } ) } FloatValidator::Finite => { quote!( if !val.is_finite() { - return Err(#error_type_name::FiniteViolated); + return Err(#error_type_path::FiniteViolated); } ) } @@ -117,7 +117,7 @@ where .collect(); quote!( - fn __validate__(val: &#inner_type) -> core::result::Result<(), #error_type_name> { + fn __validate__(val: &#inner_type) -> core::result::Result<(), #error_type_path> { let val = *val; #validations Ok(()) @@ -127,10 +127,10 @@ where fn gen_validation_error_type( type_name: &TypeName, - error_type_name: &ErrorTypeName, + error_type_path: &ErrorTypePath, validators: &[Self::Validator], ) -> TokenStream { - gen_validation_error_type(type_name, error_type_name, validators) + gen_validation_error_type(type_name, error_type_path, validators) } fn gen_traits( diff --git a/nutype_macros/src/float/gen/traits/mod.rs b/nutype_macros/src/float/gen/traits/mod.rs index 8e8799e..3d9c2c4 100644 --- a/nutype_macros/src/float/gen/traits/mod.rs +++ b/nutype_macros/src/float/gen/traits/mod.rs @@ -163,7 +163,7 @@ fn gen_implemented_traits( impl_traits: Vec, guard: &FloatGuard, ) -> Result { - let maybe_error_type_name = guard.maybe_error_type_name(); + let maybe_error_type_name = guard.maybe_error_type_path(); impl_traits .iter() .map(|t| match t { diff --git a/nutype_macros/src/integer/gen/error.rs b/nutype_macros/src/integer/gen/error.rs index 6d75ce1..1894e90 100644 --- a/nutype_macros/src/integer/gen/error.rs +++ b/nutype_macros/src/integer/gen/error.rs @@ -4,17 +4,17 @@ use quote::{quote, ToTokens}; use super::super::models::IntegerValidator; use crate::common::{ gen::error::gen_impl_error_trait, - models::{ErrorTypeName, TypeName}, + models::{ErrorTypePath, TypeName}, }; pub fn gen_validation_error_type( type_name: &TypeName, - error_type_name: &ErrorTypeName, + error_type_path: &ErrorTypePath, validators: &[IntegerValidator], ) -> TokenStream { - let definition = gen_definition(error_type_name, validators); - let impl_display_trait = gen_impl_display_trait(type_name, error_type_name, validators); - let impl_error_trait = gen_impl_error_trait(error_type_name); + let definition = gen_definition(error_type_path, validators); + let impl_display_trait = gen_impl_display_trait(type_name, error_type_path, validators); + let impl_error_trait = gen_impl_error_trait(error_type_path); quote! { #[derive(Debug, Clone, PartialEq, Eq)] @@ -26,7 +26,7 @@ pub fn gen_validation_error_type( } fn gen_definition( - error_type_name: &ErrorTypeName, + error_type_path: &ErrorTypePath, validators: &[IntegerValidator], ) -> TokenStream { let error_variants: TokenStream = validators @@ -52,7 +52,7 @@ fn gen_definition( quote! { #[allow(clippy::enum_variant_names)] - pub enum #error_type_name { + pub enum #error_type_path { #error_variants } } @@ -60,29 +60,29 @@ fn gen_definition( fn gen_impl_display_trait( type_name: &TypeName, - error_type_name: &ErrorTypeName, + error_type_path: &ErrorTypePath, validators: &[IntegerValidator], ) -> TokenStream { let match_arms = validators.iter().map(|validator| match validator { IntegerValidator::Greater(val) => quote! { - #error_type_name::GreaterViolated => write!(f, "{} is too small. The value must be greater than {:#?}.", stringify!(#type_name), #val) + #error_type_path::GreaterViolated => write!(f, "{} is too small. The value must be greater than {:#?}.", stringify!(#type_name), #val) }, IntegerValidator::GreaterOrEqual(val) => quote! { - #error_type_name::GreaterOrEqualViolated => write!(f, "{} is too small. The value must be greater or equal to {:#?}.", stringify!(#type_name), #val) + #error_type_path::GreaterOrEqualViolated => write!(f, "{} is too small. The value must be greater or equal to {:#?}.", stringify!(#type_name), #val) }, IntegerValidator::Less(val) => quote! { - #error_type_name::LessViolated=> write!(f, "{} is too big. The value must be less than {:#?}.", stringify!(#type_name), #val) + #error_type_path::LessViolated=> write!(f, "{} is too big. The value must be less than {:#?}.", stringify!(#type_name), #val) }, IntegerValidator::LessOrEqual(val) => quote! { - #error_type_name::LessOrEqualViolated=> write!(f, "{} is too big. The value must be less or equal to {:#?}.", stringify!(#type_name), #val) + #error_type_path::LessOrEqualViolated=> write!(f, "{} is too big. The value must be less or equal to {:#?}.", stringify!(#type_name), #val) }, IntegerValidator::Predicate(_) => quote! { - #error_type_name::PredicateViolated => write!(f, "{} failed the predicate test.", stringify!(#type_name)) + #error_type_path::PredicateViolated => write!(f, "{} failed the predicate test.", stringify!(#type_name)) }, }); quote! { - impl ::core::fmt::Display for #error_type_name { + impl ::core::fmt::Display for #error_type_path { fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result { match self { #(#match_arms,)* diff --git a/nutype_macros/src/integer/gen/mod.rs b/nutype_macros/src/integer/gen/mod.rs index 6dbd866..bdb189b 100644 --- a/nutype_macros/src/integer/gen/mod.rs +++ b/nutype_macros/src/integer/gen/mod.rs @@ -24,7 +24,7 @@ use crate::common::{ traits::GeneratedTraits, GenerateNewtype, }, - models::{ErrorTypeName, Guard, TypeName}, + models::{ErrorTypePath, Guard, TypeName}, }; impl GenerateNewtype for IntegerNewtype @@ -64,7 +64,7 @@ where fn gen_fn_validate( inner_type: &Self::InnerType, - error_type_name: &ErrorTypeName, + error_type_path: &ErrorTypePath, validators: &[Self::Validator], ) -> TokenStream { let validations: TokenStream = validators @@ -73,35 +73,35 @@ where IntegerValidator::Less(exclusive_upper_bound) => { quote!( if val >= #exclusive_upper_bound { - return Err(#error_type_name::LessViolated); + return Err(#error_type_path::LessViolated); } ) } IntegerValidator::LessOrEqual(max) => { quote!( if val > #max { - return Err(#error_type_name::LessOrEqualViolated); + return Err(#error_type_path::LessOrEqualViolated); } ) } IntegerValidator::Greater(exclusive_lower_bound) => { quote!( if val <= #exclusive_lower_bound { - return Err(#error_type_name::GreaterViolated); + return Err(#error_type_path::GreaterViolated); } ) } IntegerValidator::GreaterOrEqual(min) => { quote!( if val < #min { - return Err(#error_type_name::GreaterOrEqualViolated); + return Err(#error_type_path::GreaterOrEqualViolated); } ) } IntegerValidator::Predicate(custom_is_valid_fn) => { quote!( if !(#custom_is_valid_fn)(&val) { - return Err(#error_type_name::PredicateViolated); + return Err(#error_type_path::PredicateViolated); } ) } @@ -109,7 +109,7 @@ where .collect(); quote!( - fn __validate__(val: &#inner_type) -> ::core::result::Result<(), #error_type_name> { + fn __validate__(val: &#inner_type) -> ::core::result::Result<(), #error_type_path> { let val = *val; #validations Ok(()) @@ -119,10 +119,10 @@ where fn gen_validation_error_type( type_name: &TypeName, - error_type_name: &ErrorTypeName, + error_type_path: &ErrorTypePath, validators: &[Self::Validator], ) -> TokenStream { - gen_validation_error_type(type_name, error_type_name, validators) + gen_validation_error_type(type_name, error_type_path, validators) } fn gen_traits( diff --git a/nutype_macros/src/integer/gen/traits/arbitrary.rs b/nutype_macros/src/integer/gen/traits/arbitrary.rs index b9e0640..4f35fac 100644 --- a/nutype_macros/src/integer/gen/traits/arbitrary.rs +++ b/nutype_macros/src/integer/gen/traits/arbitrary.rs @@ -77,7 +77,7 @@ fn guard_to_boundary( } Validation::Standard { validators, - error_type_name: _, + error_type_path: _, } => { // Apply the validators to the boundaries. // Since the validators were already validated, it's guaranteed that they're not diff --git a/nutype_macros/src/integer/gen/traits/mod.rs b/nutype_macros/src/integer/gen/traits/mod.rs index 6dfecf7..cc804ab 100644 --- a/nutype_macros/src/integer/gen/traits/mod.rs +++ b/nutype_macros/src/integer/gen/traits/mod.rs @@ -183,7 +183,7 @@ fn gen_implemented_traits( maybe_default_value: Option, guard: &IntegerGuard, ) -> Result { - let maybe_error_type_name = guard.maybe_error_type_name(); + let maybe_error_type_name = guard.maybe_error_type_path(); impl_traits .iter() .map(|t| match t { diff --git a/nutype_macros/src/string/gen/error.rs b/nutype_macros/src/string/gen/error.rs index 8c955ba..2d0b32e 100644 --- a/nutype_macros/src/string/gen/error.rs +++ b/nutype_macros/src/string/gen/error.rs @@ -4,19 +4,19 @@ use quote::quote; use crate::{ common::{ gen::error::gen_impl_error_trait, - models::{ErrorTypeName, TypeName}, + models::{ErrorTypePath, TypeName}, }, string::models::StringValidator, }; pub fn gen_validation_error_type( type_name: &TypeName, - error_type_name: &ErrorTypeName, + error_type_path: &ErrorTypePath, validators: &[StringValidator], ) -> TokenStream { - let definition = gen_definition(error_type_name, validators); - let impl_display_trait = gen_impl_display_trait(type_name, error_type_name, validators); - let impl_error_trait = gen_impl_error_trait(error_type_name); + let definition = gen_definition(error_type_path, validators); + let impl_display_trait = gen_impl_display_trait(type_name, error_type_path, validators); + let impl_error_trait = gen_impl_error_trait(error_type_path); quote! { #[derive(Debug, Clone, PartialEq, Eq)] @@ -27,7 +27,7 @@ pub fn gen_validation_error_type( } } -fn gen_definition(error_type_name: &ErrorTypeName, validators: &[StringValidator]) -> TokenStream { +fn gen_definition(error_type_path: &ErrorTypePath, validators: &[StringValidator]) -> TokenStream { let error_variants: TokenStream = validators .iter() .map(|validator| match validator { @@ -51,7 +51,7 @@ fn gen_definition(error_type_name: &ErrorTypeName, validators: &[StringValidator quote! { #[allow(clippy::enum_variant_names)] - pub enum #error_type_name { + pub enum #error_type_path { #error_variants } } @@ -59,29 +59,29 @@ fn gen_definition(error_type_name: &ErrorTypeName, validators: &[StringValidator fn gen_impl_display_trait( type_name: &TypeName, - error_type_name: &ErrorTypeName, + error_type_path: &ErrorTypePath, validators: &[StringValidator], ) -> TokenStream { let match_arms = validators.iter().map(|validator| match validator { StringValidator::LenCharMax(len_char_max) => quote! { - #error_type_name::LenCharMaxViolated => write!(f, "{} is too long. The value length must be less than {:#?} character(s).", stringify!(#type_name), #len_char_max) + #error_type_path::LenCharMaxViolated => write!(f, "{} is too long. The value length must be less than {:#?} character(s).", stringify!(#type_name), #len_char_max) }, StringValidator::LenCharMin(len_char_min) => quote! { - #error_type_name::LenCharMinViolated => write!(f, "{} is too short. The value length must be more than {:#?} character(s).", stringify!(#type_name), #len_char_min) + #error_type_path::LenCharMinViolated => write!(f, "{} is too short. The value length must be more than {:#?} character(s).", stringify!(#type_name), #len_char_min) }, StringValidator::NotEmpty => quote! { - #error_type_name::NotEmptyViolated => write!(f, "{} is empty.", stringify!(#type_name)) + #error_type_path::NotEmptyViolated => write!(f, "{} is empty.", stringify!(#type_name)) }, StringValidator::Predicate(_) => quote! { - #error_type_name::PredicateViolated => write!(f, "{} failed the predicate test.", stringify!(#type_name)) + #error_type_path::PredicateViolated => write!(f, "{} failed the predicate test.", stringify!(#type_name)) }, StringValidator::Regex(_) => quote! { - #error_type_name::RegexViolated => write!(f, "{} violated the regular expression.", stringify!(#type_name)) + #error_type_path::RegexViolated => write!(f, "{} violated the regular expression.", stringify!(#type_name)) }, }); quote! { - impl ::core::fmt::Display for #error_type_name { + impl ::core::fmt::Display for #error_type_path { fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result { match self { #(#match_arms,)* diff --git a/nutype_macros/src/string/gen/mod.rs b/nutype_macros/src/string/gen/mod.rs index 8b2ecef..83be78f 100644 --- a/nutype_macros/src/string/gen/mod.rs +++ b/nutype_macros/src/string/gen/mod.rs @@ -14,7 +14,7 @@ use crate::{ tests::gen_test_should_have_valid_default_value, traits::GeneratedTraits, GenerateNewtype, }, - models::{ErrorTypeName, Guard, TypeName}, + models::{ErrorTypePath, Guard, TypeName}, }, string::models::{RegexDef, StringInnerType, StringSanitizer, StringValidator}, }; @@ -81,7 +81,7 @@ impl GenerateNewtype for StringNewtype { fn gen_fn_validate( _inner_type: &Self::InnerType, - error_type_name: &ErrorTypeName, + error_type_path: &ErrorTypePath, validators: &[Self::Validator], ) -> TokenStream { // Indicates that `chars_count` variable needs to be set, which is used within @@ -95,7 +95,7 @@ impl GenerateNewtype for StringNewtype { requires_chars_count = true; quote!( if chars_count > #max_len { - return Err(#error_type_name::LenCharMaxViolated); + return Err(#error_type_path::LenCharMaxViolated); } ) } @@ -103,21 +103,21 @@ impl GenerateNewtype for StringNewtype { requires_chars_count = true; quote!( if chars_count < #min_len { - return Err(#error_type_name::LenCharMinViolated); + return Err(#error_type_path::LenCharMinViolated); } ) } StringValidator::NotEmpty => { quote!( if val.is_empty() { - return Err(#error_type_name::NotEmptyViolated); + return Err(#error_type_path::NotEmptyViolated); } ) } StringValidator::Predicate(typed_custom_function) => { quote!( if !(#typed_custom_function)(&val) { - return Err(#error_type_name::PredicateViolated); + return Err(#error_type_path::PredicateViolated); } ) } @@ -129,7 +129,7 @@ impl GenerateNewtype for StringNewtype { // not clashes with anything import with `use super::*`. static __NUTYPE_REGEX__: ::std::sync::LazyLock<::regex::Regex> = ::std::sync::LazyLock::new(|| ::regex::Regex::new(#regex_str_lit).expect("Nutype failed to a build a regex")); if !__NUTYPE_REGEX__.is_match(&val) { - return Err(#error_type_name::RegexViolated); + return Err(#error_type_path::RegexViolated); } ) @@ -137,7 +137,7 @@ impl GenerateNewtype for StringNewtype { RegexDef::Path(regex_path) => { quote!( if !#regex_path.is_match(&val) { - return Err(#error_type_name::RegexViolated); + return Err(#error_type_path::RegexViolated); } ) } @@ -155,7 +155,7 @@ impl GenerateNewtype for StringNewtype { }; quote!( - fn __validate__(val: &str) -> ::core::result::Result<(), #error_type_name> { + fn __validate__(val: &str) -> ::core::result::Result<(), #error_type_path> { #chars_count_if_required #validations Ok(()) @@ -165,10 +165,10 @@ impl GenerateNewtype for StringNewtype { fn gen_validation_error_type( type_name: &TypeName, - error_type_name: &ErrorTypeName, + error_type_path: &ErrorTypePath, validators: &[Self::Validator], ) -> TokenStream { - gen_validation_error_type(type_name, error_type_name, validators) + gen_validation_error_type(type_name, error_type_path, validators) } fn gen_traits( diff --git a/nutype_macros/src/string/gen/traits/mod.rs b/nutype_macros/src/string/gen/traits/mod.rs index e94b283..697d01f 100644 --- a/nutype_macros/src/string/gen/traits/mod.rs +++ b/nutype_macros/src/string/gen/traits/mod.rs @@ -15,7 +15,7 @@ use crate::{ gen_impl_trait_try_from, split_into_generatable_traits, GeneratableTrait, GeneratableTraits, GeneratedTraits, }, - models::{ErrorTypeName, TypeName}, + models::{ErrorTypePath, TypeName}, }, string::models::{StringDeriveTrait, StringGuard, StringInnerType}, }; @@ -176,7 +176,7 @@ fn gen_implemented_traits( guard: &StringGuard, ) -> Result { let inner_type = StringInnerType; - let maybe_error_type_name = guard.maybe_error_type_name(); + let maybe_error_type_name = guard.maybe_error_type_path(); impl_traits .iter() @@ -225,7 +225,7 @@ fn gen_implemented_traits( fn gen_impl_from_str( type_name: &TypeName, - maybe_error_type_name: Option<&ErrorTypeName>, + maybe_error_type_name: Option<&ErrorTypePath>, ) -> TokenStream { if let Some(error_type_name) = maybe_error_type_name { quote! { @@ -265,7 +265,7 @@ fn gen_impl_from_str_and_string(type_name: &TypeName) -> TokenStream { fn gen_impl_try_from( type_name: &TypeName, - maybe_error_type_name: Option<&ErrorTypeName>, + maybe_error_type_name: Option<&ErrorTypePath>, ) -> TokenStream { let generics = Generics::default(); let impl_try_from_string = diff --git a/test_suite/tests/any.rs b/test_suite/tests/any.rs index d40a718..fc374b2 100644 --- a/test_suite/tests/any.rs +++ b/test_suite/tests/any.rs @@ -930,12 +930,16 @@ mod custom_error { use thiserror::Error; #[nutype( - validate(with = validate_decent_collection, error = DecentCollectionError), + validate(with = validate_decent_collection, error = namespaced_error::DecentCollectionError), derive(Debug, PartialEq, AsRef), )] struct DecentCollection(Vec); - fn validate_decent_collection(collection: &[T]) -> Result<(), DecentCollectionError> { + fn validate_decent_collection( + collection: &[T], + ) -> Result<(), namespaced_error::DecentCollectionError> { + use namespaced_error::DecentCollectionError; + if collection.len() < 3 { Err(DecentCollectionError::TooShort) } else if collection.len() > 10 { @@ -945,17 +949,25 @@ mod custom_error { } } - #[derive(Error, Debug, PartialEq)] - enum DecentCollectionError { - #[error("Collection is too short.")] - TooShort, + // NOTE: The error is within the module is on purpose to ensure that `error = namespaced_error::DecentCollectionError` + // works as expected. + mod namespaced_error { + use super::*; + + #[derive(Error, Debug, PartialEq)] + pub enum DecentCollectionError { + #[error("Collection is too short.")] + TooShort, - #[error("Collection is too long.")] - TooLong, + #[error("Collection is too long.")] + TooLong, + } } #[test] fn test_custom_error() { + use namespaced_error::DecentCollectionError; + assert_eq!( DecentCollection::try_new(vec![1, 2]), Err(DecentCollectionError::TooShort)