-
Notifications
You must be signed in to change notification settings - Fork 144
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Trait validation #225
Trait validation #225
Changes from 7 commits
babd938
02bdd69
5836898
bd9e89f
8bd86e8
de20a94
ee04dd0
a77bdc9
0790ded
3277682
66380b6
34c12ee
ba0e745
090abc5
63559f8
9aeb298
77520dc
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,29 +1,39 @@ | ||
use crate::traits::HasLen; | ||
use std::{borrow::Cow, collections::{HashMap, HashSet, BTreeMap, BTreeSet}}; | ||
|
||
#[cfg(feature = "indexmap")] | ||
use indexmap::{IndexMap, IndexSet}; | ||
|
||
/// Validates the length of the value given. | ||
/// If the validator has `equal` set, it will ignore any `min` and `max` value. | ||
/// | ||
/// If you apply it on String, don't forget that the length can be different | ||
/// from the number of visual characters for Unicode | ||
#[must_use] | ||
pub fn validate_length<T: HasLen>( | ||
pub fn validate_length<T: ValidateLength>( | ||
value: T, | ||
min: Option<u64>, | ||
max: Option<u64>, | ||
equal: Option<u64>, | ||
) -> bool { | ||
let val_length = value.length(); | ||
value.validate_length(min, max, equal) | ||
} | ||
|
||
fn validate_length_for_trait( | ||
length: u64, | ||
min: Option<u64>, | ||
max: Option<u64>, | ||
equal: Option<u64>, | ||
) -> bool { | ||
if let Some(eq) = equal { | ||
return val_length == eq; | ||
return length == eq; | ||
} else { | ||
if let Some(m) = min { | ||
if val_length < m { | ||
if length < m { | ||
return false; | ||
} | ||
} | ||
if let Some(m) = max { | ||
if val_length > m { | ||
if length > m { | ||
return false; | ||
} | ||
} | ||
|
@@ -32,11 +42,126 @@ pub fn validate_length<T: HasLen>( | |
true | ||
} | ||
|
||
pub trait ValidateLength { | ||
fn validate_length(&self, min: Option<u64>, max: Option<u64>, equal: Option<u64>) -> bool; | ||
} | ||
|
||
impl ValidateLength for String { | ||
fn validate_length(&self, min: Option<u64>, max: Option<u64>, equal: Option<u64>) -> bool { | ||
validate_length_for_trait(self.chars().count() as u64, min, max, equal) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What made you decide to go with There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I used There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Strings should use There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @Keats interesting, what is the advantage of that over There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. fn main() {
let txt = "日本語";
println!("len {:?} vs chars {:?}", txt.len(), txt.chars().count());
} will print
In this case no one reasonable will think of len as number of bytes used ( There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Wow very interesting! Makes sense to use chars count then |
||
} | ||
} | ||
|
||
impl<'a> ValidateLength for &'a String { | ||
fn validate_length(&self, min: Option<u64>, max: Option<u64>, equal: Option<u64>) -> bool { | ||
validate_length_for_trait(self.chars().count() as u64, min, max, equal) | ||
} | ||
} | ||
|
||
impl<'a> ValidateLength for &'a str { | ||
fn validate_length(&self, min: Option<u64>, max: Option<u64>, equal: Option<u64>) -> bool { | ||
validate_length_for_trait(self.chars().count() as u64, min, max, equal) | ||
} | ||
} | ||
|
||
impl<'a> ValidateLength for Cow<'a, str> { | ||
fn validate_length(&self, min: Option<u64>, max: Option<u64>, equal: Option<u64>) -> bool { | ||
validate_length_for_trait(self.chars().count() as u64, min, max, equal) | ||
} | ||
} | ||
|
||
impl<T> ValidateLength for Vec<T> { | ||
fn validate_length(&self, min: Option<u64>, max: Option<u64>, equal: Option<u64>) -> bool { | ||
validate_length_for_trait(self.len() as u64, min, max, equal) | ||
} | ||
} | ||
|
||
impl<'a, T> ValidateLength for &'a Vec<T> { | ||
fn validate_length(&self, min: Option<u64>, max: Option<u64>, equal: Option<u64>) -> bool { | ||
validate_length_for_trait(self.len() as u64, min, max, equal) | ||
} | ||
} | ||
|
||
impl<T> ValidateLength for &[T] { | ||
fn validate_length(&self, min: Option<u64>, max: Option<u64>, equal: Option<u64>) -> bool { | ||
validate_length_for_trait(self.len() as u64, min, max, equal) | ||
} | ||
} | ||
|
||
impl<T, const N: usize> ValidateLength for [T; N] { | ||
fn validate_length(&self, min: Option<u64>, max: Option<u64>, equal: Option<u64>) -> bool { | ||
validate_length_for_trait(N as u64, min, max, equal) | ||
} | ||
} | ||
|
||
impl<T, const N: usize> ValidateLength for &[T; N] { | ||
fn validate_length(&self, min: Option<u64>, max: Option<u64>, equal: Option<u64>) -> bool { | ||
validate_length_for_trait(N as u64, min, max, equal) | ||
} | ||
} | ||
|
||
impl<'a, K, V, S> ValidateLength for &'a HashMap<K, V, S> { | ||
fn validate_length(&self, min: Option<u64>, max: Option<u64>, equal: Option<u64>) -> bool { | ||
validate_length_for_trait(self.len() as u64, min, max, equal) | ||
} | ||
} | ||
|
||
impl<K, V, S> ValidateLength for HashMap<K, V, S> { | ||
fn validate_length(&self, min: Option<u64>, max: Option<u64>, equal: Option<u64>) -> bool { | ||
validate_length_for_trait(self.len() as u64, min, max, equal) | ||
} | ||
} | ||
|
||
impl<'a, T, S> ValidateLength for &'a HashSet<T, S> { | ||
fn validate_length(&self, min: Option<u64>, max: Option<u64>, equal: Option<u64>) -> bool { | ||
validate_length_for_trait(self.len() as u64, min, max, equal) | ||
} | ||
} | ||
|
||
impl<'a, K, V> ValidateLength for &'a BTreeMap<K, V> { | ||
fn validate_length(&self, min: Option<u64>, max: Option<u64>, equal: Option<u64>) -> bool { | ||
validate_length_for_trait(self.len() as u64, min, max, equal) | ||
} | ||
} | ||
|
||
impl<'a, T> ValidateLength for &'a BTreeSet<T> { | ||
fn validate_length(&self, min: Option<u64>, max: Option<u64>, equal: Option<u64>) -> bool { | ||
validate_length_for_trait(self.len() as u64, min, max, equal) | ||
} | ||
} | ||
|
||
impl<T> ValidateLength for BTreeSet<T> { | ||
fn validate_length(&self, min: Option<u64>, max: Option<u64>, equal: Option<u64>) -> bool { | ||
validate_length_for_trait(self.len() as u64, min, max, equal) | ||
} | ||
} | ||
|
||
#[cfg(feature = "indexmap")] | ||
impl<'a, K, V> ValidateLength for &'a IndexMap<K, V> { | ||
fn validate_length(&self, min: Option<u64>, max: Option<u64>, equal: Option<u64>) -> bool { | ||
validate_length_for_trait(self.len() as u64, min, max, equal) | ||
} | ||
} | ||
|
||
#[cfg(feature = "indexmap")] | ||
impl<'a, T> ValidateLength for &'a IndexSet<T> { | ||
fn validate_length(&self, min: Option<u64>, max: Option<u64>, equal: Option<u64>) -> bool { | ||
validate_length_for_trait(self.len() as u64, min, max, equal) | ||
} | ||
} | ||
|
||
#[cfg(feature = "indexmap")] | ||
impl<T> ValidateLength for IndexSet<T> { | ||
fn validate_length(&self, min: Option<u64>, max: Option<u64>, equal: Option<u64>) -> bool { | ||
validate_length_for_trait(self.len() as u64, min, max, equal) | ||
} | ||
} | ||
|
||
#[cfg(test)] | ||
mod tests { | ||
use std::borrow::Cow; | ||
|
||
use super::validate_length; | ||
use crate::{validate_length, validation::length::ValidateLength}; | ||
|
||
#[test] | ||
fn test_validate_length_equal_overrides_min_max() { | ||
|
@@ -76,4 +201,44 @@ mod tests { | |
fn test_validate_length_unicode_chars() { | ||
assert!(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))); | ||
} | ||
|
||
#[test] | ||
fn test_validate_length_trait_string_min_max() { | ||
assert!(String::from("hello").validate_length(Some(1), Some(10), None)); | ||
} | ||
|
||
#[test] | ||
fn test_validate_length_trait_string_min_only() { | ||
assert!(!String::from("hello").validate_length(Some(10), None, None)); | ||
} | ||
|
||
#[test] | ||
fn test_validate_length_trait_string_max_only() { | ||
assert!(!String::from("hello").validate_length(None, Some(1), None)); | ||
} | ||
|
||
#[test] | ||
fn test_validate_length_trait_cow() { | ||
let test: Cow<'static, str> = "hello".into(); | ||
assert!(test.validate_length(None, None, Some(5))); | ||
|
||
let test: Cow<'static, str> = String::from("hello").into(); | ||
assert!(test.validate_length(None, None, Some(5))); | ||
} | ||
|
||
#[test] | ||
fn test_validate_length_trait_vec() { | ||
assert!(vec![1, 2, 3].validate_length(None, None, Some(3))); | ||
} | ||
|
||
#[test] | ||
fn test_validate_length_trait_unicode_chars() { | ||
assert!(String::from("日本").validate_length(None, None, Some(2))); | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thoughts on letting crates handle validator support themselves like they do for serde?