use std::any::{type_name, Any}; use std::error::Error; use std::fmt::Debug; use std::marker::PhantomData; use anyhow::{bail, ensure, Result}; use crate::any::ConfigData; use crate::Value; pub trait Validator: 'static + Debug { fn validate(&self, val: Value) -> Result; } pub trait Ty: Sized + Clone + 'static { fn from_value(val: Value) -> Result; fn to_value(&self) -> Value; } #[derive(Clone, Copy)] pub struct IntegerRangeValidator { pub min: isize, pub max: isize, ty: PhantomData, } impl IntegerRangeValidator where E: Debug, T: TryInto, { pub fn new(min: T, max: T) -> Self { Self { min: min.try_into().unwrap(), max: max.try_into().unwrap(), ty: PhantomData, } } } impl Debug for IntegerRangeValidator { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_struct("IntegerRangeValidator") .field("min", &self.min) .field("max", &self.max) .field("ty", &type_name::()) .finish() } } impl IntegerRangeValidator where E: Error + Sync + Send + 'static, T: Any + TryFrom, { pub fn validate(&self, val: Value) -> Result { let IntegerRangeValidator { min, max, .. } = *self; let Value::Int(val) = val else { bail!("expected an integer") }; ensure!( min <= val && val <= max, "expected an integer between {min} and {max} (got {val})", ); Ok(T::try_from(val)?) } } impl Validator for IntegerRangeValidator where E: Error + Sync + Send + 'static, T: Any + TryFrom, { fn validate(&self, val: Value) -> Result { Ok(ConfigData::new(self.validate(val))) } } macro_rules! integer_tys { ($($ty: ident),*) => { $( impl Ty for $ty { fn to_value(&self) -> Value { Value::Int((*self).try_into().unwrap()) } fn from_value(val: Value) -> Result { IntegerRangeValidator::new($ty::MIN, $ty::MAX).validate(val) } } )* }; } integer_tys! { i8, i16, i32, isize, u8, u16, u32 } impl Ty for usize { fn to_value(&self) -> Value { Value::Int((*self).try_into().unwrap()) } fn from_value(val: Value) -> Result { IntegerRangeValidator::new(0usize, isize::MAX as usize).validate(val) } } impl Ty for u64 { fn to_value(&self) -> Value { Value::Int((*self).try_into().unwrap()) } fn from_value(val: Value) -> Result { IntegerRangeValidator::new(0u64, isize::MAX as u64).validate(val) } } impl Ty for bool { fn to_value(&self) -> Value { Value::Bool(*self) } fn from_value(val: Value) -> Result { let Value::Bool(val) = val else { bail!("expected a boolean") }; Ok(val) } } impl Ty for Box { fn to_value(&self) -> Value { Value::String(self.clone().into_string()) } fn from_value(val: Value) -> Result { let Value::String(val) = val else { bail!("expected a string") }; Ok(val.into_boxed_str()) } } impl Ty for char { fn to_value(&self) -> Value { Value::String(self.to_string()) } fn from_value(val: Value) -> Result { let Value::String(val) = val else { bail!("expected a string") }; ensure!( val.chars().count() == 1, "expecet a single character (got {val:?})" ); Ok(val.chars().next().unwrap()) } } impl Ty for std::string::String { fn to_value(&self) -> Value { Value::String(self.clone()) } fn from_value(val: Value) -> Result { let Value::String(val) = val else { bail!("expected a string") }; Ok(val) } } impl Ty for Option { fn to_value(&self) -> Value { match self { Some(_) => todo!(), None => todo!(), } } fn from_value(val: Value) -> Result { if val == Value::Null { return Ok(None); } Ok(Some(T::from_value(val)?)) } } impl Ty for Box { fn from_value(val: Value) -> Result { Ok(Box::new(T::from_value(val)?)) } fn to_value(&self) -> Value { T::to_value(self) } } impl Ty for indexmap::IndexMap, T, ahash::RandomState> { fn from_value(val: Value) -> Result { let Value::Map(map) = val else { bail!("expected a map"); }; map.into_iter() .map(|(k, v)| Ok((k, T::from_value(v)?))) .collect() } fn to_value(&self) -> Value { let map = self .iter() .map(|(k, v)| (k.clone(), v.to_value())) .collect(); Value::Map(Box::new(map)) } } impl Ty for Box<[T]> { fn to_value(&self) -> Value { Value::List(self.iter().map(T::to_value).collect()) } fn from_value(val: Value) -> Result { let Value::List(val) = val else { bail!("expected a list") }; val.iter().cloned().map(T::from_value).collect() } } impl Ty for serde_json::Value { fn from_value(val: Value) -> Result { Ok(val.into()) } fn to_value(&self) -> Value { self.into() } } pub(super) struct StaticValidator { pub(super) ty: PhantomData, } impl Debug for StaticValidator { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_struct("StaticValidator") .field("ty", &type_name::()) .finish() } } impl Validator for StaticValidator { fn validate(&self, val: Value) -> Result { let val = ::from_value(val)?; Ok(ConfigData::new(val)) } } pub struct TyValidator { pub(super) ty: PhantomData, f: F, } impl Debug for TyValidator { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_struct("TyValidator") .field("ty", &type_name::()) .finish() } } impl Validator for TyValidator where T: Ty, F: Fn(&T) -> anyhow::Result<()> + 'static, { fn validate(&self, val: Value) -> Result { let val = ::from_value(val)?; (self.f)(&val)?; Ok(ConfigData::new(val)) } } pub fn ty_validator(f: F) -> impl Validator where T: Ty, F: Fn(&T) -> anyhow::Result<()> + 'static, { TyValidator { ty: PhantomData, f } } pub fn regex_str_validator() -> impl Validator { ty_validator(|val: &crate::String| { regex_syntax::parse(val)?; Ok(()) }) }