diff --git a/validator/src/lib.rs b/validator/src/lib.rs index da38576..e1b7dd7 100644 --- a/validator/src/lib.rs +++ b/validator/src/lib.rs @@ -82,7 +82,7 @@ pub use validation::regex::ValidateRegex; pub use validation::required::ValidateRequired; pub use validation::urls::ValidateUrl; -pub use traits::{HasLen, Validate, ValidateArgs}; +pub use traits::{Validate, ValidateArgs}; pub use types::{ValidationError, ValidationErrors, ValidationErrorsKind}; #[cfg(feature = "derive")] diff --git a/validator/src/traits.rs b/validator/src/traits.rs index f28b484..ddf1239 100644 --- a/validator/src/traits.rs +++ b/validator/src/traits.rs @@ -1,149 +1,5 @@ -use std::borrow::Cow; -use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet}; - -#[cfg(feature = "indexmap")] -use indexmap::{IndexMap, IndexSet}; - use crate::types::ValidationErrors; -/// Trait to implement if one wants to make the `length` validator -/// work for more types -/// -/// A bit sad it's not there by default in Rust -pub trait HasLen { - fn length(&self) -> u64; -} - -impl HasLen for String { - fn length(&self) -> u64 { - self.chars().count() as u64 - } -} - -impl<'a> HasLen for &'a String { - fn length(&self) -> u64 { - self.chars().count() as u64 - } -} - -impl<'a> HasLen for &'a str { - fn length(&self) -> u64 { - self.chars().count() as u64 - } -} - -impl<'a> HasLen for Cow<'a, str> { - fn length(&self) -> u64 { - self.len() as u64 - } -} - -impl HasLen for Vec { - fn length(&self) -> u64 { - self.len() as u64 - } -} - -impl<'a, T> HasLen for &'a Vec { - fn length(&self) -> u64 { - self.len() as u64 - } -} - -impl HasLen for &[T] { - fn length(&self) -> u64 { - self.len() as u64 - } -} - -impl HasLen for [T; N] { - fn length(&self) -> u64 { - N as u64 - } -} - -impl HasLen for &[T; N] { - fn length(&self) -> u64 { - N as u64 - } -} - -impl<'a, K, V, S> HasLen for &'a HashMap { - fn length(&self) -> u64 { - self.len() as u64 - } -} - -impl HasLen for HashMap { - fn length(&self) -> u64 { - self.len() as u64 - } -} - -impl<'a, T, S> HasLen for &'a HashSet { - fn length(&self) -> u64 { - self.len() as u64 - } -} - -impl HasLen for HashSet { - fn length(&self) -> u64 { - self.len() as u64 - } -} - -impl<'a, K, V> HasLen for &'a BTreeMap { - fn length(&self) -> u64 { - self.len() as u64 - } -} - -impl HasLen for BTreeMap { - fn length(&self) -> u64 { - self.len() as u64 - } -} - -impl<'a, T> HasLen for &'a BTreeSet { - fn length(&self) -> u64 { - self.len() as u64 - } -} - -impl HasLen for BTreeSet { - fn length(&self) -> u64 { - self.len() as u64 - } -} - -#[cfg(feature = "indexmap")] -impl<'a, K, V> HasLen for &'a IndexMap { - fn length(&self) -> u64 { - self.len() as u64 - } -} - -#[cfg(feature = "indexmap")] -impl HasLen for IndexMap { - fn length(&self) -> u64 { - self.len() as u64 - } -} - -#[cfg(feature = "indexmap")] -impl<'a, T> HasLen for &'a IndexSet { - fn length(&self) -> u64 { - self.len() as u64 - } -} - -#[cfg(feature = "indexmap")] -impl HasLen for IndexSet { - fn length(&self) -> u64 { - self.len() as u64 - } -} - /// This is the original trait that was implemented by deriving `Validate`. It will still be /// implemented for struct validations that don't take custom arguments. The call is being /// forwarded to the `ValidateArgs<'v_a>` trait. @@ -153,7 +9,7 @@ pub trait Validate { impl Validate for &T { fn validate(&self) -> Result<(), ValidationErrors> { - T::validate(*self) + T::validate(self) } } @@ -168,8 +24,8 @@ pub trait ValidateArgs<'v_a> { } impl<'v_a, T, U> ValidateArgs<'v_a> for Option -where - T: ValidateArgs<'v_a, Args = U>, + where + T: ValidateArgs<'v_a, Args=U>, { type Args = U; diff --git a/validator/src/validation/contains.rs b/validator/src/validation/contains.rs index d8235af..a385c1d 100644 --- a/validator/src/validation/contains.rs +++ b/validator/src/validation/contains.rs @@ -12,57 +12,30 @@ impl ValidateContains for String { } } -impl ValidateContains for Option { +impl ValidateContains for Option + where T: ValidateContains { fn validate_contains(&self, needle: &str) -> bool { if let Some(v) = self { - v.contains(needle) + v.validate_contains(needle) } else { true } } } -impl ValidateContains for Option> { +impl ValidateContains for &T + where T: ValidateContains { fn validate_contains(&self, needle: &str) -> bool { - if let Some(v) = self { - if let Some(v) = v { - v.contains(needle) - } else { - true - } - } else { - true - } - } -} - -impl ValidateContains for &String { - fn validate_contains(&self, needle: &str) -> bool { - self.contains(needle) - } -} - -impl ValidateContains for Option<&String> { - fn validate_contains(&self, needle: &str) -> bool { - if let Some(v) = self { - v.contains(needle) - } else { - true - } + T::validate_contains(self, needle) } } -impl ValidateContains for Option> { +impl<'cow, T> ValidateContains for Cow<'cow, T> + where T: ToOwned + ?Sized, + for<'a> &'a T: ValidateContains +{ fn validate_contains(&self, needle: &str) -> bool { - if let Some(v) = self { - if let Some(v) = v { - v.contains(needle) - } else { - true - } - } else { - true - } + self.as_ref().validate_contains(needle) } } @@ -72,120 +45,12 @@ impl<'a> ValidateContains for &'a str { } } -impl<'a> ValidateContains for Option<&'a str> { - fn validate_contains(&self, needle: &str) -> bool { - if let Some(v) = self { - v.contains(needle) - } else { - true - } - } -} - -impl<'a> ValidateContains for Option> { - fn validate_contains(&self, needle: &str) -> bool { - if let Some(v) = self { - if let Some(v) = v { - v.contains(needle) - } else { - true - } - } else { - true - } - } -} - -impl<'a> ValidateContains for Cow<'a, str> { - fn validate_contains(&self, needle: &str) -> bool { - self.contains(needle) - } -} - -impl<'a> ValidateContains for Option> { - fn validate_contains(&self, needle: &str) -> bool { - if let Some(v) = self { - v.contains(needle) - } else { - true - } - } -} - -impl<'a> ValidateContains for Option>> { - fn validate_contains(&self, needle: &str) -> bool { - if let Some(v) = self { - if let Some(v) = v { - v.contains(needle) - } else { - true - } - } else { - true - } - } -} - impl ValidateContains for HashMap { fn validate_contains(&self, needle: &str) -> bool { self.contains_key(needle) } } -impl ValidateContains for Option> { - fn validate_contains(&self, needle: &str) -> bool { - if let Some(v) = self { - v.contains_key(needle) - } else { - true - } - } -} - -impl ValidateContains for Option>> { - fn validate_contains(&self, needle: &str) -> bool { - if let Some(v) = self { - if let Some(v) = v { - v.contains_key(needle) - } else { - true - } - } else { - true - } - } -} - -impl<'a, S, H: BuildHasher> ValidateContains for &'a HashMap { - fn validate_contains(&self, needle: &str) -> bool { - self.contains_key(needle) - } -} - -impl<'a, S, H: BuildHasher> ValidateContains for Option<&'a HashMap> { - fn validate_contains(&self, needle: &str) -> bool { - if let Some(v) = self { - v.contains_key(needle) - } else { - true - } - } -} - -impl<'a, S, H: BuildHasher> ValidateContains for Option>> { - fn validate_contains(&self, needle: &str) -> bool { - if let Some(v) = self { - if let Some(v) = v { - v.contains_key(needle) - } else { - true - } - } else { - true - } - } -} - #[cfg(test)] mod tests { use std::borrow::Cow; diff --git a/validator/src/validation/email.rs b/validator/src/validation/email.rs index 22624ee..58fb02e 100644 --- a/validator/src/validation/email.rs +++ b/validator/src/validation/email.rs @@ -3,7 +3,7 @@ use lazy_static::lazy_static; use regex::Regex; use std::borrow::Cow; -use crate::{HasLen, ValidateIp}; +use crate::{ValidateIp}; lazy_static! { // Regex from the specs @@ -39,7 +39,7 @@ fn validate_domain_part(domain_part: &str) -> bool { /// that are unfamiliar to most users. pub trait ValidateEmail { fn validate_email(&self) -> bool { - let val = if let Some(v) = self.as_email_string() { v } else { return true }; + let val = if let Some(v) = self.as_email_string() { v } else { return true; }; if val.is_empty() || !val.contains('@') { return false; @@ -53,7 +53,7 @@ pub trait ValidateEmail { // according to RFC5321 the max length of the local part is 64 characters // and the max length of the domain part is 255 characters // https://datatracker.ietf.org/doc/html/rfc5321#section-4.5.3.1.1 - if user_part.length() > 64 || domain_part.length() > 255 { + if user_part.chars().count() > 64 || domain_part.chars().count() > 255 { return false; } @@ -75,47 +75,28 @@ pub trait ValidateEmail { fn as_email_string(&self) -> Option>; } -impl ValidateEmail for String { - fn as_email_string(&self) -> Option> { - Some(Cow::from(self)) - } -} - -impl ValidateEmail for Option { - fn as_email_string(&self) -> Option> { - self.as_ref().map(Cow::from) - } -} - -impl ValidateEmail for Option> { +impl ValidateEmail for &T + where T: ValidateEmail { fn as_email_string(&self) -> Option> { - if let Some(u) = self { - u.as_ref().map(Cow::from) - } else { - None - } + T::as_email_string(self) } } -impl ValidateEmail for &String { +impl ValidateEmail for String { fn as_email_string(&self) -> Option> { - Some(Cow::from(self.as_str())) + Some(Cow::from(self)) } } -impl ValidateEmail for Option<&String> { +impl ValidateEmail for Option + where + T: ValidateEmail, { fn as_email_string(&self) -> Option> { - self.as_ref().map(|u| Cow::from(*u)) - } -} + let Some(u) = self else { + return None; + }; -impl ValidateEmail for Option> { - fn as_email_string(&self) -> Option> { - if let Some(u) = self { - u.as_ref().map(|u| Cow::from(*u)) - } else { - None - } + T::as_email_string(u) } } @@ -125,44 +106,12 @@ impl<'a> ValidateEmail for &'a str { } } -impl<'a> ValidateEmail for Option<&'a str> { - fn as_email_string(&self) -> Option> { - self.as_ref().map(|u| Cow::from(*u)) - } -} - -impl<'a> ValidateEmail for Option> { - fn as_email_string(&self) -> Option> { - if let Some(u) = self { - u.as_ref().map(|u| Cow::from(*u)) - } else { - None - } - } -} - impl ValidateEmail for Cow<'_, str> { fn as_email_string(&self) -> Option> { Some(self.clone()) } } -impl ValidateEmail for Option> { - fn as_email_string(&self) -> Option> { - self.as_ref().map(|u| u.clone()) - } -} - -impl ValidateEmail for Option>> { - fn as_email_string(&self) -> Option> { - if let Some(u) = self { - u.as_ref().map(|u| u.clone()) - } else { - None - } - } -} - #[cfg(test)] mod tests { use std::borrow::Cow; diff --git a/validator/src/validation/length.rs b/validator/src/validation/length.rs index fdbb995..9f64065 100644 --- a/validator/src/validation/length.rs +++ b/validator/src/validation/length.rs @@ -1,6 +1,9 @@ use std::{ borrow::Cow, - collections::{BTreeMap, BTreeSet, HashMap, HashSet}, + collections::{BTreeMap, BTreeSet, HashMap, HashSet, VecDeque}, + rc::Rc, + sync::Arc, + cell::{Ref, RefMut}, }; #[cfg(feature = "indexmap")] @@ -12,8 +15,8 @@ use indexmap::{IndexMap, IndexSet}; /// If you apply it on String, don't forget that the length can be different /// from the number of visual characters for Unicode pub trait ValidateLength -where - T: PartialEq + PartialOrd, + where + T: PartialEq + PartialOrd, { fn validate_length(&self, min: Option, max: Option, equal: Option) -> bool { if let Some(length) = self.length() { @@ -40,439 +43,88 @@ where fn length(&self) -> Option; } -impl ValidateLength for String { - fn length(&self) -> Option { - Some(self.chars().count() as u64) - } -} - -impl ValidateLength for Option { - fn length(&self) -> Option { - self.as_ref().map(|s| s.chars().count() as u64) - } -} - -impl ValidateLength for Option> { - fn length(&self) -> Option { - if let Some(s) = self { - s.as_ref().map(|s| s.chars().count() as u64) - } else { - None - } - } -} - -impl<'a> ValidateLength for &'a String { - fn length(&self) -> Option { - Some(self.chars().count() as u64) - } -} - -impl<'a> ValidateLength for Option<&'a String> { - fn length(&self) -> Option { - self.as_ref().map(|s| s.chars().count() as u64) - } -} - -impl<'a> ValidateLength for Option> { - fn length(&self) -> Option { - self.flatten().map(|s| s.chars().count() as u64) - } -} - -impl<'a> ValidateLength for &'a str { - fn length(&self) -> Option { - Some(self.chars().count() as u64) - } -} - -impl<'a> ValidateLength for Option<&'a str> { - fn length(&self) -> Option { - self.as_ref().map(|s| s.chars().count() as u64) - } -} - -impl<'a> ValidateLength for Option> { - fn length(&self) -> Option { - self.flatten().map(|s| s.chars().count() as u64) - } -} - -impl<'a> ValidateLength for Cow<'a, str> { - fn length(&self) -> Option { - Some(self.chars().count() as u64) - } -} - -impl<'a> ValidateLength for Option> { - fn length(&self) -> Option { - self.as_ref().map(|s| s.chars().count() as u64) - } -} - -impl<'a> ValidateLength for Option>> { - fn length(&self) -> Option { - if let Some(s) = self { - s.as_ref().map(|s| s.chars().count() as u64) - } else { - None - } - } -} - -impl ValidateLength for Vec { - fn length(&self) -> Option { - Some(self.len() as u64) - } -} - -impl ValidateLength for Option> { - fn length(&self) -> Option { - self.as_ref().map(|v| v.len() as u64) - } -} - -impl ValidateLength for Option>> { - fn length(&self) -> Option { - if let Some(v) = self { - v.as_ref().map(|v| v.len() as u64) - } else { - None - } - } -} - -impl<'a, T> ValidateLength for &'a Vec { - fn length(&self) -> Option { - Some(self.len() as u64) - } -} - -impl<'a, T> ValidateLength for Option<&'a Vec> { - fn length(&self) -> Option { - self.as_ref().map(|v| v.len() as u64) - } -} - -impl<'a, T> ValidateLength for Option>> { - fn length(&self) -> Option { - if let Some(v) = self { - v.as_ref().map(|v| v.len() as u64) - } else { - None - } - } -} - -impl ValidateLength for &[T] { - fn length(&self) -> Option { - Some(self.len() as u64) - } -} - -impl ValidateLength for Option<&[T]> { - fn length(&self) -> Option { - self.as_ref().map(|v| v.len() as u64) - } -} - -impl ValidateLength for Option> { - fn length(&self) -> Option { - if let Some(v) = self { - v.as_ref().map(|v| v.len() as u64) - } else { - None - } - } -} - -impl ValidateLength for [T; N] { - fn length(&self) -> Option { - Some(N as u64) - } -} - -impl ValidateLength for Option<[T; N]> { - fn length(&self) -> Option { - Some(N as u64) - } -} - -impl ValidateLength for Option> { - fn length(&self) -> Option { - Some(N as u64) - } -} - -impl ValidateLength for &[T; N] { - fn length(&self) -> Option { - Some(N as u64) - } -} - -impl ValidateLength for Option<&[T; N]> { - fn length(&self) -> Option { - Some(N as u64) - } -} - -impl ValidateLength for Option> { - fn length(&self) -> Option { - Some(N as u64) - } -} - -impl ValidateLength for HashMap { - fn length(&self) -> Option { - Some(self.len() as u64) - } -} - -impl ValidateLength for Option> { - fn length(&self) -> Option { - self.as_ref().map(|v| v.len() as u64) - } -} - -impl ValidateLength for Option>> { - fn length(&self) -> Option { - if let Some(v) = self { - v.as_ref().map(|v| v.len() as u64) - } else { - None - } - } -} - -impl<'a, K, V, S> ValidateLength for &'a HashMap { - fn length(&self) -> Option { - Some(self.len() as u64) - } -} - -impl<'a, K, V, S> ValidateLength for Option<&'a HashMap> { - fn length(&self) -> Option { - self.as_ref().map(|v| v.len() as u64) - } -} - -impl<'a, K, V, S> ValidateLength for Option>> { - fn length(&self) -> Option { - if let Some(v) = self { - v.as_ref().map(|v| v.len() as u64) - } else { - None - } - } -} - -impl ValidateLength for HashSet { - fn length(&self) -> Option { - Some(self.len() as u64) - } -} - -impl ValidateLength for Option> { - fn length(&self) -> Option { - self.as_ref().map(|v| v.len() as u64) - } -} - -impl ValidateLength for Option>> { - fn length(&self) -> Option { - if let Some(v) = self { - v.as_ref().map(|v| v.len() as u64) - } else { - None - } - } -} - -impl<'a, T, S> ValidateLength for &'a HashSet { - fn length(&self) -> Option { - Some(self.len() as u64) - } -} - -impl<'a, T, S> ValidateLength for Option<&'a HashSet> { - fn length(&self) -> Option { - self.as_ref().map(|v| v.len() as u64) - } -} - -impl<'a, T, S> ValidateLength for Option>> { - fn length(&self) -> Option { - if let Some(v) = self { - v.as_ref().map(|v| v.len() as u64) - } else { - None - } - } -} - -impl<'a, K, V> ValidateLength for &'a BTreeMap { - fn length(&self) -> Option { - Some(self.len() as u64) - } -} - -impl<'a, K, V> ValidateLength for Option<&'a BTreeMap> { - fn length(&self) -> Option { - self.as_ref().map(|v| v.len() as u64) - } -} - -impl<'a, K, V> ValidateLength for Option>> { - fn length(&self) -> Option { - if let Some(v) = self { - v.as_ref().map(|v| v.len() as u64) - } else { - None - } - } -} - -impl ValidateLength for BTreeSet { - fn length(&self) -> Option { - Some(self.len() as u64) - } -} - -impl ValidateLength for Option> { - fn length(&self) -> Option { - self.as_ref().map(|v| v.len() as u64) - } -} - -impl ValidateLength for Option>> { - fn length(&self) -> Option { - if let Some(v) = self { - v.as_ref().map(|v| v.len() as u64) - } else { - None - } - } -} - -impl<'a, T> ValidateLength for &'a BTreeSet { - fn length(&self) -> Option { - Some(self.len() as u64) - } -} - -impl<'a, T> ValidateLength for Option<&'a BTreeSet> { - fn length(&self) -> Option { - self.as_ref().map(|v| v.len() as u64) - } -} - -impl<'a, T> ValidateLength for Option>> { - fn length(&self) -> Option { - if let Some(v) = self { - v.as_ref().map(|v| v.len() as u64) - } else { - None +macro_rules! validate_type_that_derefs { + ($type_:ty) => { + impl ValidateLength for $type_ + where T: ValidateLength { + fn length(&self) -> Option { + T::length(self) + } } - } -} - -#[cfg(feature = "indexmap")] -impl ValidateLength for IndexMap { - fn length(&self) -> Option { - Some(self.len() as u64) - } + }; } -#[cfg(feature = "indexmap")] -impl ValidateLength for Option> { - fn length(&self) -> Option { - self.as_ref().map(|v| v.len() as u64) - } -} +validate_type_that_derefs!(&T); +validate_type_that_derefs!(Arc); +validate_type_that_derefs!(Box); +validate_type_that_derefs!(Rc); +validate_type_that_derefs!(Ref<'_, T>); +validate_type_that_derefs!(RefMut<'_, T>); -#[cfg(feature = "indexmap")] -impl ValidateLength for Option>> { - fn length(&self) -> Option { - if let Some(v) = self { - v.as_ref().map(|v| v.len() as u64) - } else { - None +macro_rules! validate_type_with_chars { + ($type_:ty) => { + impl ValidateLength for $type_ { + fn length(&self) -> Option { + Some(self.chars().count() as u64) + } } - } -} - -#[cfg(feature = "indexmap")] -impl<'a, K, V> ValidateLength for &'a IndexMap { - fn length(&self) -> Option { - Some(self.len() as u64) - } + }; } -#[cfg(feature = "indexmap")] -impl<'a, K, V> ValidateLength for Option<&'a IndexMap> { - fn length(&self) -> Option { - self.as_ref().map(|v| v.len() as u64) - } -} +validate_type_with_chars!(str); +validate_type_with_chars!(&str); +validate_type_with_chars!(String); -#[cfg(feature = "indexmap")] -impl<'a, K, V> ValidateLength for Option>> { - fn length(&self) -> Option { - if let Some(v) = self { - v.as_ref().map(|v| v.len() as u64) - } else { - None +macro_rules! validate_type_with_len { + ($type_:ty) => { + validate_type_with_len!($type_,); + }; + ($type_:ty, $($generic:ident),*$(,)*) => { + impl<$($generic),*> ValidateLength for $type_ { + fn length(&self) -> Option { + Some(self.len() as u64) + } } - } + }; } +validate_type_with_len!([T], T); +validate_type_with_len!(BTreeSet, T); +validate_type_with_len!(BTreeMap, K, V); +validate_type_with_len!(HashSet, T, S); +validate_type_with_len!(HashMap, K, V, S); +validate_type_with_len!(Vec, T); +validate_type_with_len!(VecDeque, T); #[cfg(feature = "indexmap")] -impl ValidateLength for IndexSet { - fn length(&self) -> Option { - Some(self.len() as u64) - } -} - +validate_type_with_len!(IndexSet, T); #[cfg(feature = "indexmap")] -impl ValidateLength for Option> { - fn length(&self) -> Option { - self.as_ref().map(|v| v.len() as u64) - } -} +validate_type_with_len!(IndexMap, K, V); -#[cfg(feature = "indexmap")] -impl ValidateLength for Option>> { +impl ValidateLength for Cow<'_, T> + where + T: ToOwned + ?Sized, + for<'a> &'a T: ValidateLength, +{ fn length(&self) -> Option { - if let Some(v) = self { - v.as_ref().map(|v| v.len() as u64) - } else { - None - } + self.as_ref().length() } } -#[cfg(feature = "indexmap")] -impl<'a, T> ValidateLength for &'a IndexSet { +impl ValidateLength for Option + where T: ValidateLength, +{ fn length(&self) -> Option { - Some(self.len() as u64) - } -} + let Some(s) = self else { + return None; + }; -#[cfg(feature = "indexmap")] -impl<'a, T> ValidateLength for Option<&'a IndexSet> { - fn length(&self) -> Option { - self.as_ref().map(|v| v.len() as u64) + T::length(s) } } -#[cfg(feature = "indexmap")] -impl<'a, T> ValidateLength for Option>> { +impl ValidateLength for [T; N] { fn length(&self) -> Option { - if let Some(v) = self { - v.as_ref().map(|v| v.len() as u64) - } else { - None - } + Some(N as u64) } } @@ -521,6 +173,15 @@ mod tests { assert!("日本".validate_length(None, None, Some(2))); } + #[test] + fn test_validate_length_cow_unicode_chars() { + let test: Cow<'static, str> = "日本".into(); + assert!(test.validate_length(None, None, Some(2))); + + let test: Cow<'static, str> = String::from("日本").into(); + assert!(test.validate_length(None, None, Some(2))); + } + #[test] fn test_validate_length_trait_equal_overrides_min_max() { assert!(String::from("hello").validate_length(Some(1), Some(2), Some(5))); diff --git a/validator/src/validation/regex.rs b/validator/src/validation/regex.rs index 70942dc..702ce19 100644 --- a/validator/src/validation/regex.rs +++ b/validator/src/validation/regex.rs @@ -14,9 +14,10 @@ impl AsRegex for Regex { } } -impl AsRegex for &Regex { +impl AsRegex for &T + where T: AsRegex { fn as_regex(&self) -> Cow { - Cow::Borrowed(self) + T::as_regex(self) } } @@ -54,122 +55,41 @@ pub trait ValidateRegex { fn validate_regex(&self, regex: impl AsRegex) -> bool; } -impl ValidateRegex for String { +impl ValidateRegex for &T + where T: ValidateRegex { fn validate_regex(&self, regex: impl AsRegex) -> bool { - regex.as_regex().is_match(self) + T::validate_regex(self, regex) } } -impl ValidateRegex for Option { +impl ValidateRegex for Option + where T: ValidateRegex { fn validate_regex(&self, regex: impl AsRegex) -> bool { if let Some(h) = self { - regex.as_regex().is_match(h) + T::validate_regex(h, regex) } else { true } } } -impl ValidateRegex for Option> { +impl<'cow, T> ValidateRegex for Cow<'cow, T> + where T: ToOwned + ?Sized, + for<'a> &'a T: ValidateRegex +{ fn validate_regex(&self, regex: impl AsRegex) -> bool { - if let Some(h) = self { - if let Some(h) = h { - regex.as_regex().is_match(h) - } else { - true - } - } else { - true - } + self.as_ref().validate_regex(regex) } } -impl ValidateRegex for &String { +impl ValidateRegex for String { fn validate_regex(&self, regex: impl AsRegex) -> bool { regex.as_regex().is_match(self) } } -impl ValidateRegex for Option<&String> { - fn validate_regex(&self, regex: impl AsRegex) -> bool { - if let Some(h) = self { - regex.as_regex().is_match(h) - } else { - true - } - } -} - -impl ValidateRegex for Option> { - fn validate_regex(&self, regex: impl AsRegex) -> bool { - if let Some(h) = self { - if let Some(h) = h { - regex.as_regex().is_match(h) - } else { - true - } - } else { - true - } - } -} - impl ValidateRegex for &str { fn validate_regex(&self, regex: impl AsRegex) -> bool { regex.as_regex().is_match(self) } } - -impl ValidateRegex for Option<&str> { - fn validate_regex(&self, regex: impl AsRegex) -> bool { - if let Some(h) = self { - regex.as_regex().is_match(h) - } else { - true - } - } -} - -impl ValidateRegex for Option> { - fn validate_regex(&self, regex: impl AsRegex) -> bool { - if let Some(h) = self { - if let Some(h) = h { - regex.as_regex().is_match(h) - } else { - true - } - } else { - true - } - } -} - -impl ValidateRegex for Cow<'_, str> { - fn validate_regex(&self, regex: impl AsRegex) -> bool { - regex.as_regex().is_match(self) - } -} - -impl ValidateRegex for Option> { - fn validate_regex(&self, regex: impl AsRegex) -> bool { - if let Some(h) = self { - regex.as_regex().is_match(h) - } else { - true - } - } -} - -impl ValidateRegex for Option>> { - fn validate_regex(&self, regex: impl AsRegex) -> bool { - if let Some(h) = self { - if let Some(h) = h { - regex.as_regex().is_match(h) - } else { - true - } - } else { - true - } - } -} diff --git a/validator/src/validation/urls.rs b/validator/src/validation/urls.rs index ba088b3..8af2c0a 100644 --- a/validator/src/validation/urls.rs +++ b/validator/src/validation/urls.rs @@ -1,4 +1,9 @@ -use std::borrow::Cow; +use std::{ + borrow::Cow, + rc::Rc, + sync::Arc, + cell::{Ref, RefMut}, +}; use url::Url; /// Validates whether the string given is a url @@ -14,79 +19,52 @@ pub trait ValidateUrl { fn as_url_string(&self) -> Option>; } -impl ValidateUrl for String { - fn as_url_string(&self) -> Option> { - Some(Cow::from(self)) - } -} - -impl ValidateUrl for Option { - fn as_url_string(&self) -> Option> { - self.as_ref().map(Cow::from) - } -} - -impl ValidateUrl for Option> { - fn as_url_string(&self) -> Option> { - if let Some(u) = self { - u.as_ref().map(Cow::from) - } else { - None +macro_rules! validate_type_that_derefs { + ($type_:ty) => { + impl ValidateUrl for $type_ + where T: ValidateUrl { + fn as_url_string(&self) -> Option> { + T::as_url_string(self) + } } - } -} - -impl ValidateUrl for &String { - fn as_url_string(&self) -> Option> { - Some(Cow::from(self.as_str())) - } -} - -impl ValidateUrl for Option<&String> { - fn as_url_string(&self) -> Option> { - self.as_ref().map(|u| Cow::from(*u)) - } + }; } -impl ValidateUrl for Option> { - fn as_url_string(&self) -> Option> { - self.flatten().map(Cow::from) - } +validate_type_that_derefs!(&T); +validate_type_that_derefs!(Arc); +validate_type_that_derefs!(Box); +validate_type_that_derefs!(Rc); +validate_type_that_derefs!(Ref<'_, T>); +validate_type_that_derefs!(RefMut<'_, T>); + +macro_rules! validate_type_of_str { + ($type_:ty) => { + impl ValidateUrl for $type_ { + fn as_url_string(&self) -> Option> { + Some(Cow::Borrowed(self)) + } + } + }; } -impl<'a> ValidateUrl for &'a str { - fn as_url_string(&self) -> Option> { - Some(Cow::from(*self)) - } -} +validate_type_of_str!(str); +validate_type_of_str!(&str); +validate_type_of_str!(String); -impl<'a> ValidateUrl for Option<&'a str> { +impl ValidateUrl for Option + where T: ValidateUrl { fn as_url_string(&self) -> Option> { - self.as_ref().map(|u| Cow::from(*u)) - } -} + let Some(u) = self else { + return None; + }; -impl<'a> ValidateUrl for Option> { - fn as_url_string(&self) -> Option> { - self.flatten().map(Cow::from) + T::as_url_string(u) } } impl ValidateUrl for Cow<'_, str> { fn as_url_string(&self) -> Option> { - Some(self.clone()) - } -} - -impl ValidateUrl for Option> { - fn as_url_string(&self) -> Option> { - self.as_ref().cloned() - } -} - -impl ValidateUrl for Option>> { - fn as_url_string(&self) -> Option> { - self.as_ref().cloned().flatten() + ::as_url_string(self) } } diff --git a/validator_derive/src/types.rs b/validator_derive/src/types.rs index 93fac35..a1b24f9 100644 --- a/validator_derive/src/types.rs +++ b/validator_derive/src/types.rs @@ -37,7 +37,7 @@ pub struct ValidateField { } impl ValidateField { - pub fn validate(&self, struct_ident: &Ident, all_fields: &Vec<&Field>, current_field: &Field) { + pub fn validate(&self, struct_ident: &Ident, all_fields: &[&Field], current_field: &Field) { let field_name = self.ident.clone().expect("Field is not a named field").to_string(); let field_attrs = ¤t_field.attrs; diff --git a/validator_derive/src/utils.rs b/validator_derive/src/utils.rs index 32f86d3..e698b8c 100644 --- a/validator_derive/src/utils.rs +++ b/validator_derive/src/utils.rs @@ -129,10 +129,10 @@ pub fn quote_use_stmts(fields: &Vec) -> proc_macro2::TokenStream ) } -pub fn get_attr<'a>(attrs: &'a Vec, name: &str) -> Option<&'a Attribute> { +pub fn get_attr<'a>(attrs: &'a [Attribute], name: &str) -> Option<&'a Attribute> { attrs.iter().find(|a| match &a.meta { syn::Meta::List(list) => list.tokens.clone().into_iter().any(|t| match t { - proc_macro2::TokenTree::Ident(i) => &i.to_string() == name, + proc_macro2::TokenTree::Ident(i) => i == name, _ => false, }), _ => false,