use lazy_regex::{Lazy, Regex};
use roadblk_attr::{ConstraintType, EnumError, ModelValidatorError, RangeError, RegexError};
use std::{
collections::{HashMap, HashSet},
fmt::Display,
};
pub trait Validator<Data> {
type Error;
fn validate(data: &Data) -> Result<(), Self::Error>;
fn constraint_type() -> ConstraintType;
}
impl<D, T: Validator<D>> Validator<Option<D>> for T {
type Error = T::Error;
fn validate(data: &Option<D>) -> Result<(), Self::Error> {
if let Some(data) = data {
return T::validate(data);
}
Ok(())
}
fn constraint_type() -> ConstraintType {
T::constraint_type()
}
}
pub trait RegexValidator<V> {
fn validate(regex: &Lazy<Regex>, value: &V) -> Result<(), RegexError>;
}
impl RegexValidator<String> for String {
fn validate(regex: &Lazy<Regex>, value: &String) -> Result<(), RegexError> {
if regex.is_match(value) {
return Ok(());
}
Err(RegexError {
regex: regex.to_string(),
value: value.to_string(),
})
}
}
impl RegexValidator<Option<String>> for Option<String> {
fn validate(regex: &Lazy<Regex>, value: &Option<String>) -> Result<(), RegexError> {
if let Some(value) = value {
return <String as RegexValidator<String>>::validate(regex, value);
}
Ok(())
}
}
pub trait HasLength {
fn length(&self) -> usize;
}
impl HasLength for String {
fn length(&self) -> usize {
self.len()
}
}
impl<T> HasLength for Vec<T> {
fn length(&self) -> usize {
self.len()
}
}
impl<T> HasLength for HashSet<T> {
fn length(&self) -> usize {
self.len()
}
}
impl<K, V> HasLength for HashMap<K, V> {
fn length(&self) -> usize {
self.len()
}
}
pub trait RangeValidator {
type O: PartialOrd;
fn validate(&self, min: Self::O, max: Self::O) -> Result<(), RangeError>;
}
impl<D: NumberTrait + PartialOrd + Display> RangeValidator for D {
type O = D;
fn validate(&self, min: Self::O, max: Self::O) -> Result<(), RangeError> {
if *self < min {
return Err(RangeError {
min: format!("{min}"),
max: format!("{max}"),
value: format!("{self}"),
});
}
if *self > max {
return Err(RangeError {
min: format!("{min}"),
max: format!("{max}"),
value: format!("{self}"),
});
}
Ok(())
}
}
pub trait NumberTrait {}
impl NumberTrait for i8 {}
impl NumberTrait for i16 {}
impl NumberTrait for i32 {}
impl NumberTrait for i64 {}
impl NumberTrait for i128 {}
impl NumberTrait for u8 {}
impl NumberTrait for usize {}
impl NumberTrait for u16 {}
impl NumberTrait for u32 {}
impl NumberTrait for u64 {}
impl NumberTrait for u128 {}
impl NumberTrait for f32 {}
impl NumberTrait for f64 {}
impl<D: NumberTrait + PartialOrd + Display> RangeValidator for Option<D> {
type O = D;
fn validate(&self, min: Self::O, max: Self::O) -> Result<(), RangeError> {
if let Some(data) = self {
if *data < min {
return Err(RangeError {
min: format!("{min}"),
max: format!("{max}"),
value: format!("{data}"),
});
}
if *data > max {
return Err(RangeError {
min: format!("{min}"),
max: format!("{max}"),
value: format!("{data}"),
});
}
}
Ok(())
}
}
pub trait SelfValidator {
fn validate(&self) -> Result<(), ModelValidatorError>;
}
impl<T: SelfValidator> SelfValidator for Option<T> {
fn validate(&self) -> Result<(), ModelValidatorError> {
if let Some(data) = self {
return data.validate();
}
Ok(())
}
}
pub trait EnumValidator<'a, T> {
fn validate(data: &'a Self) -> Result<(), EnumError>;
}
impl<'a, T: TryFrom<&'a i32>> EnumValidator<'a, T> for i32 {
fn validate(data: &'a Self) -> Result<(), EnumError> {
if T::try_from(data).is_err() {
return Err(EnumError);
}
Ok(())
}
}
impl<'a, T: TryFrom<&'a i32>> EnumValidator<'a, T> for Option<i32> {
fn validate(data: &'a Self) -> Result<(), EnumError> {
if let Some(data) = data {
if T::try_from(data).is_err() {
return Err(EnumError);
}
}
Ok(())
}
}
impl<'a, T: TryFrom<&'a String>> EnumValidator<'a, T> for String {
fn validate(data: &'a Self) -> Result<(), EnumError> {
if T::try_from(data).is_err() {
return Err(EnumError);
}
Ok(())
}
}
impl<'a, T: TryFrom<&'a String>> EnumValidator<'a, T> for Option<String> {
fn validate(data: &'a Self) -> Result<(), EnumError> {
if let Some(data) = data {
if T::try_from(data).is_err() {
return Err(EnumError);
}
}
Ok(())
}
}