ivo 0.3.0

The rust implementation of the ivo schema valitator
Documentation
#![expect(type_alias_bounds)]

use futures::future::{BoxFuture, FutureExt};

use std::future::Future;

use crate::{
    __private_types::{types::BooleanResolver, ValidatorResponse},
    schema::types::{DeleteHandler, FailureHandler, FieldValue, SuccessHandler},
    types::{
        internal::types::{erase_value, parse_or_panic, ErasedValue},
        IvoConstantCtx, IvoDefaultCtx,
    },
    IvoContext, IvoErrorSanitizer, IvoRwCtxOptions, IvoShared, IvoStruct,
};

pub type TimestampResolver<T: FieldValue> = Box<dyn Fn() -> T + Send + Sync + 'static>;

pub trait IntoDeleteHandler<O: IvoStruct, CtxOptions> {
    fn into_handler(self) -> DeleteHandler<O, CtxOptions>;
}

impl<F, Fut, Data, CtxOptions> IntoDeleteHandler<Data, CtxOptions> for F
where
    Data: IvoStruct,
    F: Fn(IvoShared<Data>, IvoShared<CtxOptions>) -> Fut + Send + Sync + 'static,
    Fut: Future<Output = ()> + Send + Sync + 'static,
{
    fn into_handler(self) -> DeleteHandler<Data, CtxOptions> {
        Box::new(move |data, o| Box::pin(self(data, o)))
    }
}

pub trait IntoFailureHandler<I: IvoStruct, O: IvoStruct, CtxOptions> {
    fn into_handler(self) -> FailureHandler<I, O, CtxOptions>;
}

impl<F, Fut, I, O, CtxOptions> IntoFailureHandler<I, O, CtxOptions> for F
where
    I: IvoStruct,
    O: IvoStruct,
    F: Fn(IvoContext<I, O>, IvoShared<CtxOptions>) -> Fut + Send + Sync + 'static,
    Fut: Future<Output = ()> + Send + Sync + 'static,
{
    fn into_handler(self) -> FailureHandler<I, O, CtxOptions> {
        Box::new(move |ctx, o| Box::pin(self(ctx, o)))
    }
}

pub trait IntoSuccessHandler<I: IvoStruct, O: IvoStruct, CtxOptions> {
    fn into_handler(self) -> SuccessHandler<I, O, CtxOptions>;
}

impl<F, Fut, I, O, CtxOptions> IntoSuccessHandler<I, O, CtxOptions> for F
where
    I: IvoStruct,
    O: IvoStruct,
    F: Fn(IvoContext<I, O>, IvoShared<CtxOptions>) -> Fut + Send + Sync + 'static,
    Fut: Future<Output = ()> + Send + Sync + 'static,
{
    fn into_handler(self) -> SuccessHandler<I, O, CtxOptions> {
        Box::new(move |ctx, o| Box::pin(self(ctx, o)))
    }
}

pub trait IntoFieldValidator<
    T,
    I: IvoStruct,
    O: IvoStruct,
    CtxOptions,
    ErrorSanitizer: IvoErrorSanitizer<CtxOptions>,
>
{
    fn into_uniform(self) -> UniformValidator<I, O, CtxOptions, ErrorSanitizer::Metadata>;
}

impl<F, Fut, T, I, O, CtxOptions, ErrorSanitizer>
    IntoFieldValidator<T, I, O, CtxOptions, ErrorSanitizer> for F
where
    I: IvoStruct,
    O: IvoStruct,
    ErrorSanitizer: IvoErrorSanitizer<CtxOptions>,
    T: FieldValue,
    F: Fn(T, IvoContext<I, O>, IvoRwCtxOptions<CtxOptions>) -> Fut + Send + Sync + 'static,
    Fut: Future<Output = ValidatorResponse<T, ErrorSanitizer::Metadata>> + Send + Sync + 'static,
{
    fn into_uniform(self) -> UniformValidator<I, O, CtxOptions, ErrorSanitizer::Metadata> {
        Box::new(move |v, ctx, o| {
            Box::pin(
                self(parse_or_panic::<T>(&v, None), ctx, o)
                    .map(|result| result.map(|option| option.map(|value| erase_value(value)))),
            )
        })
    }
}

pub trait IntoVirtualSanitizer<T, I: IvoStruct, O: IvoStruct, CtxOptions> {
    fn into_uniform(self) -> UniformVirtualSanitizer<I, O, CtxOptions>;
}

impl<F, Fut, T, I, O, CtxOptions> IntoVirtualSanitizer<T, I, O, CtxOptions> for F
where
    I: IvoStruct,
    O: IvoStruct,
    T: FieldValue,
    F: Fn(T, IvoContext<I, O>, IvoRwCtxOptions<CtxOptions>) -> Fut + Send + Sync + 'static,
    Fut: Future<Output = T> + Send + Sync + 'static,
{
    fn into_uniform(self) -> UniformVirtualSanitizer<I, O, CtxOptions> {
        Box::new(move |v, ctx, o| {
            Box::pin(self(parse_or_panic(&v, None), ctx, o).map(|v| erase_value(v)))
        })
    }
}

pub trait IntoInitRequiredErrorResolver<I: IvoStruct, O: IvoStruct, CtxOptions> {
    fn into_resolver(self) -> InitRequiredResolver<I, CtxOptions>;
}

impl<F, Fut, I: IvoStruct, O: IvoStruct, CtxOptions> IntoInitRequiredErrorResolver<I, O, CtxOptions>
    for F
where
    F: Fn(I::Partial, IvoRwCtxOptions<CtxOptions>) -> Fut + Send + Sync + 'static,
    Fut: Future<Output = String> + Send + 'static,
{
    fn into_resolver(self) -> InitRequiredResolver<I, CtxOptions> {
        Box::new(move |raw_input, o| Box::pin(self(raw_input, o)))
    }
}

pub trait IntoRequiredResolver<I: IvoStruct, O: IvoStruct, CtxOptions> {
    fn into_resolver(self) -> RequiredResolver<I, O, CtxOptions>;
}

impl<F, Fut, I: IvoStruct, O: IvoStruct, CtxOptions> IntoRequiredResolver<I, O, CtxOptions> for F
where
    F: Fn(IvoContext<I, O>, IvoRwCtxOptions<CtxOptions>) -> Fut + Send + Sync + 'static,
    Fut: Future<Output = RequiredError> + Send + 'static,
{
    fn into_resolver(self) -> RequiredResolver<I, O, CtxOptions> {
        Box::new(move |ctx, o| Box::pin(self(ctx, o)))
    }
}

pub trait IntoUniformResolver<T, I: IvoStruct, O: IvoStruct, CtxOptions> {
    fn into_uniform(self) -> UniformResolver<I, O, CtxOptions>;
}

impl<F, Fut, T, I, O, CtxOptions> IntoUniformResolver<T, I, O, CtxOptions> for F
where
    I: IvoStruct,
    O: IvoStruct,
    T: FieldValue,
    F: Fn(IvoContext<I, O>, IvoRwCtxOptions<CtxOptions>) -> Fut + Send + Sync + 'static,
    Fut: Future<Output = T> + Send + 'static,
{
    fn into_uniform(self) -> UniformResolver<I, O, CtxOptions> {
        Box::new(move |ctx, o| Box::pin(self(ctx, o).map(|v| erase_value(v))))
    }
}

pub type DefaultValueResolver<I: IvoStruct, CtxOptions> = Box<
    dyn Fn(IvoDefaultCtx<I>, IvoRwCtxOptions<CtxOptions>) -> BoxFuture<'static, ErasedValue>
        + Send
        + Sync
        + 'static,
>;

pub enum DefaultValue<T, I: IvoStruct, CtxOptions> {
    Static(T),
    Func(DefaultValueResolver<I, CtxOptions>),
}

pub trait IntoDefaultValueResolver<T, I: IvoStruct, CtxOptions> {
    fn into_uniform(self) -> DefaultValueResolver<I, CtxOptions>;
}

impl<F, Fut, T, I: IvoStruct, CtxOptions> IntoDefaultValueResolver<T, I, CtxOptions> for F
where
    T: FieldValue,
    F: Fn(IvoDefaultCtx<I>, IvoRwCtxOptions<CtxOptions>) -> Fut + Send + Sync + 'static,
    Fut: Future<Output = T> + Send + 'static,
{
    fn into_uniform(self) -> DefaultValueResolver<I, CtxOptions> {
        Box::new(move |ctx, o| Box::pin(self(ctx, o).map(|v| erase_value(v))))
    }
}

pub type ConstantValueResolver<I: IvoStruct, O: IvoStruct, CtxOptions> = Box<
    dyn Fn(IvoConstantCtx<I, O>, IvoRwCtxOptions<CtxOptions>) -> BoxFuture<'static, ErasedValue>
        + Send
        + Sync
        + 'static,
>;

pub enum ConstantValue<T, I: IvoStruct, O: IvoStruct, CtxOptions> {
    Static(T),
    Func(ConstantValueResolver<I, O, CtxOptions>),
}

pub trait IntoConstantValueResolver<T, I: IvoStruct, O: IvoStruct, CtxOptions> {
    fn into_uniform(self) -> ConstantValueResolver<I, O, CtxOptions>;
}

impl<F, Fut, T, I: IvoStruct, O: IvoStruct, CtxOptions>
    IntoConstantValueResolver<T, I, O, CtxOptions> for F
where
    T: FieldValue,
    F: Fn(IvoConstantCtx<I, O>, IvoRwCtxOptions<CtxOptions>) -> Fut + Send + Sync + 'static,
    Fut: Future<Output = T> + Send + 'static,
{
    fn into_uniform(self) -> ConstantValueResolver<I, O, CtxOptions> {
        Box::new(move |ctx, o| Box::pin(self(ctx, o).map(|v| erase_value(v))))
    }
}

pub trait IntoBooleanResolver<I: IvoStruct, O: IvoStruct, CtxOptions> {
    fn into_resolver(self) -> BooleanResolver<I, O, CtxOptions>;
}

impl<F, Fut, I: IvoStruct, O: IvoStruct, CtxOptions> IntoBooleanResolver<I, O, CtxOptions> for F
where
    F: Fn(IvoContext<I, O>, IvoRwCtxOptions<CtxOptions>) -> Fut + Send + Sync + 'static,
    Fut: Future<Output = bool> + Send + 'static,
{
    fn into_resolver(self) -> BooleanResolver<I, O, CtxOptions> {
        Box::new(move |ctx, o| Box::pin(self(ctx, o)))
    }
}

pub type UniformValidator<I, O, CtxOptions, Metadata> = Box<
    dyn Fn(
            ErasedValue,
            IvoContext<I, O>,
            IvoRwCtxOptions<CtxOptions>,
        ) -> BoxFuture<'static, ValidatorResponse<ErasedValue, Metadata>>
        + Send
        + Sync
        + 'static,
>;

pub type UniformVirtualSanitizer<I, O, CtxOptions> = Box<
    dyn Fn(
            ErasedValue,
            IvoContext<I, O>,
            IvoRwCtxOptions<CtxOptions>,
        ) -> BoxFuture<'static, ErasedValue>
        + Send
        + Sync
        + 'static,
>;

pub type UniformResolver<I, O, CtxOptions> = Box<
    dyn Fn(IvoContext<I, O>, IvoRwCtxOptions<CtxOptions>) -> BoxFuture<'static, ErasedValue>
        + Send
        + Sync
        + 'static,
>;

pub enum IsFieldProvisionEnabled<I: IvoStruct, O: IvoStruct, CtxOptions> {
    False,
    Readonly,
    Func(BooleanResolver<I, O, CtxOptions>),
}

pub enum ComputableRequiredError<I: IvoStruct, CtxOptions> {
    Static(&'static str),
    Func(InitRequiredResolver<I, CtxOptions>),
}

pub type RequiredError = Option<String>;

pub type InitRequiredResolver<I: IvoStruct, CtxOptions> = Box<
    dyn Fn(I::Partial, IvoRwCtxOptions<CtxOptions>) -> BoxFuture<'static, String>
        + Send
        + Sync
        + 'static,
>;

pub type RequiredResolver<I: IvoStruct, O: IvoStruct, CtxOptions> = Box<
    dyn Fn(IvoContext<I, O>, IvoRwCtxOptions<CtxOptions>) -> BoxFuture<'static, RequiredError>
        + Send
        + Sync
        + 'static,
>;

pub type VirtualSanitizer<T, I, O, CtxOptions> = Box<
    dyn Fn(T, IvoContext<I, O>, IvoRwCtxOptions<CtxOptions>) -> BoxFuture<'static, T>
        + Send
        + Sync
        + 'static,
>;