#[derive(Debug, PartialEq)]
pub enum ConstraintResult {
Valid,
Err(ConstraintError),
}
#[derive(Debug, PartialEq)]
pub enum ConstraintError {
InvalidConstraint,
TooLarge,
TooSmall,
BlacklistedChar,
TooLong,
TooShort,
}
pub trait Constraint<T> {
fn validate(&self, data: &T) -> ConstraintResult;
}
pub struct StringConstraint {
pub max_len: Option<usize>,
pub min_len: Option<usize>,
pub blacklist_chars: Vec<char>,
}
impl<T> Constraint<T> for StringConstraint
where
T: AsRef<str>,
{
fn validate(&self, data: &T) -> ConstraintResult {
let data = data.as_ref();
if let Some(max_len) = self.max_len {
if data.len() > max_len {
return ConstraintResult::Err(ConstraintError::TooLong);
}
}
if let Some(min_len) = self.min_len {
if data.len() < min_len {
return ConstraintResult::Err(ConstraintError::TooShort);
}
}
for ch in data.chars() {
if self.blacklist_chars.contains(&ch) {
return ConstraintResult::Err(ConstraintError::BlacklistedChar);
}
}
ConstraintResult::Valid
}
}
pub struct NumberConstraint {
pub max: Option<i32>,
pub min: Option<i32>,
}
impl<T> Constraint<T> for NumberConstraint
where
T: Into<i32> + Clone + PartialOrd,
{
fn validate(&self, data: &T) -> ConstraintResult {
let res = i32::try_from(data.clone()).map_err(|_| ConstraintError::InvalidConstraint);
if let Err(err) = res {
return ConstraintResult::Err(err);
}
let data = res.unwrap();
if let Some(max) = self.max {
if data > max {
return ConstraintResult::Err(ConstraintError::TooLarge);
}
}
if let Some(min) = self.min {
if data < min {
return ConstraintResult::Err(ConstraintError::TooSmall);
}
}
ConstraintResult::Valid
}
}