use crate::visitor::{
PathSegment, VisitorContext, VisitorCore, VisitorMutCore, perform_visit, perform_visit_mut,
};
pub trait Visitable: Normalize + Validate {
fn type_identity(&self) -> &'static str {
std::any::type_name::<Self>()
}
fn drive(&self, _: &mut dyn VisitorCore) {}
fn drive_mut(&mut self, _: &mut dyn VisitorMutCore) {}
}
impl<T: Visitable> Visitable for Option<T> {
fn drive(&self, visitor: &mut dyn VisitorCore) {
if let Some(value) = self.as_ref() {
perform_visit(visitor, value, PathSegment::Empty);
}
}
fn drive_mut(&mut self, visitor: &mut dyn VisitorMutCore) {
if let Some(value) = self.as_mut() {
perform_visit_mut(visitor, value, PathSegment::Empty);
}
}
}
impl<T: Visitable> Visitable for Vec<T> {
fn drive(&self, visitor: &mut dyn VisitorCore) {
for (i, value) in self.iter().enumerate() {
perform_visit(visitor, value, i);
}
}
fn drive_mut(&mut self, visitor: &mut dyn VisitorMutCore) {
for (i, value) in self.iter_mut().enumerate() {
perform_visit_mut(visitor, value, i);
}
}
}
impl<T: Visitable> Visitable for Box<T> {
fn type_identity(&self) -> &'static str {
(**self).type_identity()
}
fn drive(&self, visitor: &mut dyn VisitorCore) {
(**self).drive(visitor);
}
fn drive_mut(&mut self, visitor: &mut dyn VisitorMutCore) {
(**self).drive_mut(visitor);
}
}
macro_rules! impl_primitive_visitable {
($($ty:ty),* $(,)?) => {
$(impl Visitable for $ty {})*
};
}
impl_primitive_visitable!(
i8,
i16,
i32,
i64,
i128,
u8,
u16,
u32,
u64,
u128,
f32,
f64,
bool,
String,
crate::schema::Account,
crate::schema::Blob,
crate::schema::Date,
crate::schema::Decimal,
crate::schema::Duration,
crate::schema::Float32,
crate::schema::Float64,
crate::schema::Principal,
crate::schema::Subaccount,
crate::schema::Timestamp,
crate::schema::U256,
crate::schema::Ulid,
crate::schema::Unit,
);
pub trait Normalize: NormalizeAuto + NormalizeCustom {}
impl<T> Normalize for T where T: NormalizeAuto + NormalizeCustom {}
pub trait NormalizeAuto {
fn normalize_self(&mut self, _ctx: &mut dyn VisitorContext) {}
}
impl<T: NormalizeAuto> NormalizeAuto for Option<T> {}
impl<T: NormalizeAuto> NormalizeAuto for Vec<T> {}
impl<T: NormalizeAuto + ?Sized> NormalizeAuto for Box<T> {
fn normalize_self(&mut self, ctx: &mut dyn VisitorContext) {
(**self).normalize_self(ctx);
}
}
impl_primitive!(NormalizeAuto);
pub trait NormalizeCustom {
fn normalize_custom(&mut self, _ctx: &mut dyn VisitorContext) {}
}
impl<T: NormalizeCustom> NormalizeCustom for Option<T> {}
impl<T: NormalizeCustom> NormalizeCustom for Vec<T> {}
impl<T: NormalizeCustom + ?Sized> NormalizeCustom for Box<T> {
fn normalize_custom(&mut self, ctx: &mut dyn VisitorContext) {
(**self).normalize_custom(ctx);
}
}
impl_primitive!(NormalizeCustom);
pub trait Validate: ValidateAuto + ValidateCustom {}
impl<T> Validate for T where T: ValidateAuto + ValidateCustom {}
pub trait ValidateAuto {
fn validate_self(&self, _ctx: &mut dyn VisitorContext) {}
}
impl<T: ValidateAuto> ValidateAuto for Option<T> {}
impl<T: ValidateAuto> ValidateAuto for Vec<T> {}
impl<T: ValidateAuto + ?Sized> ValidateAuto for Box<T> {
fn validate_self(&self, ctx: &mut dyn VisitorContext) {
(**self).validate_self(ctx);
}
}
impl_primitive!(ValidateAuto);
pub trait ValidateCustom {
fn validate_custom(&self, _ctx: &mut dyn VisitorContext) {}
}
impl<T: ValidateCustom> ValidateCustom for Option<T> {}
impl<T: ValidateCustom> ValidateCustom for Vec<T> {}
impl<T: ValidateCustom + ?Sized> ValidateCustom for Box<T> {
fn validate_custom(&self, ctx: &mut dyn VisitorContext) {
(**self).validate_custom(ctx);
}
}
impl_primitive!(ValidateCustom);
pub trait Normalizer<T> {
fn normalize(&self, value: &mut T) -> Result<(), String>;
fn normalize_with_context(
&self,
value: &mut T,
ctx: &mut dyn VisitorContext,
) -> Result<(), String> {
let _ = ctx;
self.normalize(value)
}
}
pub trait Validator<T: ?Sized> {
fn validate(&self, value: &T, ctx: &mut dyn VisitorContext);
}
#[cfg(test)]
mod tests;