use std::fmt::Display;
use std::marker::PhantomData;
use std::str::FromStr;
#[cfg(feature = "typing")]
use crate::typing::Type;
pub trait Validator {
fn validate(&self, value: &str) -> Result<(), String>;
#[cfg(feature = "typing")]
fn valid_type(&self) -> Option<Type> {
None
}
}
#[derive(Default)]
pub struct TypeValidator<T>(PhantomData<T>);
impl<T> TypeValidator<T> {
#[inline]
pub fn new() -> Self {
TypeValidator(PhantomData)
}
}
impl<T: 'static> Validator for TypeValidator<T>
where
T: FromStr,
{
fn validate(&self, value: &str) -> Result<(), String> {
match T::from_str(value) {
Ok(_) => Ok(()),
Err(_) => Err(format!("`{}`", value)),
}
}
#[cfg(feature = "typing")]
fn valid_type(&self) -> Option<Type> {
Some(Type::of::<T>())
}
}
pub struct RangeValidator<T>(T, T);
impl<T> RangeValidator<T>
where
T: FromStr + PartialOrd + Display,
{
#[inline]
pub fn new(min: T, max: T) -> Self {
assert!(min < max, "min cannot be greater than max");
RangeValidator(min, max)
}
}
impl<T: 'static> Validator for RangeValidator<T>
where
T: FromStr + PartialOrd + Display,
{
fn validate(&self, value: &str) -> Result<(), String> {
match T::from_str(value) {
Err(_) => Err(format!("`{}`", value)),
Ok(n) => {
if n >= self.0 && n <= self.1 {
Ok(())
} else {
Err(format!("{} is out of range: {}..{}", n, self.0, self.1))
}
}
}
}
#[cfg(feature = "typing")]
fn valid_type(&self) -> Option<Type> {
Some(Type::of::<T>())
}
}
impl<F> Validator for F
where
F: Fn(&str) -> std::result::Result<(), String>,
{
fn validate(&self, value: &str) -> Result<(), String> {
match (self)(value) {
Ok(_) => Ok(()),
Err(msg) => Err(msg),
}
}
}
#[inline]
pub fn validate_type<T: 'static + FromStr>() -> TypeValidator<T> {
TypeValidator::new()
}
#[inline]
pub fn validate_range<T: 'static>(min: T, max: T) -> RangeValidator<T>
where
T: FromStr + PartialOrd + Display,
{
RangeValidator::new(min, max)
}