ivo 0.0.2

The schema valitator that brings user stories to life, inspired by ivo on npm
Documentation
#![allow(type_alias_bounds)]

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

use std::future::Future;

use crate::{
    erase_value, parse_or_panic,
    types::{DeleteHandler, ErasedValue, FailureHandler, IvoFieldValue, SuccessHandler},
    IvoErrorTool, IvoSchemaStruct, SharedData, SharedIvoContext, SharedIvoMiniContext,
    SharedRwCtxOptions, ValidatorResponse,
};

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

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

impl<F, Fut, Data, CtxOptions> IntoDeleteHandler<Data, CtxOptions> for F
where
    Data: IvoSchemaStruct,
    F: Fn(SharedData<Data>, SharedData<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: IvoSchemaStruct, O: IvoSchemaStruct, CtxOptions> {
    fn into_handler(self) -> FailureHandler<I, O, CtxOptions>;
}

impl<F, Fut, I, O, CtxOptions> IntoFailureHandler<I, O, CtxOptions> for F
where
    I: IvoSchemaStruct,
    O: IvoSchemaStruct,
    F: Fn(SharedIvoContext<I, O>, SharedData<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: IvoSchemaStruct, O: IvoSchemaStruct, CtxOptions> {
    fn into_handler(self) -> SuccessHandler<I, O, CtxOptions>;
}

impl<F, Fut, I, O, CtxOptions> IntoSuccessHandler<I, O, CtxOptions> for F
where
    I: IvoSchemaStruct,
    O: IvoSchemaStruct,
    F: Fn(SharedIvoContext<I, O>, SharedData<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: IvoSchemaStruct,
    O: IvoSchemaStruct,
    CtxOptions,
    ErrorTool: IvoErrorTool,
>
{
    fn into_uniform(self) -> UniformValidator<I, O, CtxOptions, ErrorTool::FieldMetadata>;
}

impl<F, Fut, T, I, O, CtxOptions, ErrorTool> IntoFieldValidator<T, I, O, CtxOptions, ErrorTool>
    for F
where
    I: IvoSchemaStruct,
    O: IvoSchemaStruct,
    ErrorTool: IvoErrorTool,
    T: IvoFieldValue,
    F: Fn(T, SharedIvoContext<I, O>, SharedRwCtxOptions<CtxOptions>) -> Fut + Send + Sync + 'static,
    Fut: Future<Output = ValidatorResponse<T, ErrorTool::FieldMetadata>> + Send + Sync + 'static,
{
    fn into_uniform(self) -> UniformValidator<I, O, CtxOptions, ErrorTool::FieldMetadata> {
        Box::new(move |v, ctx, o| {
            Box::pin(self(parse_or_panic::<T>(&v, None), ctx, o).map(|r| r.map(|v| erase_value(v))))
        })
    }
}

pub trait IntoVirtualSanitizer<T, I: IvoSchemaStruct, O: IvoSchemaStruct, 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: IvoSchemaStruct,
    O: IvoSchemaStruct,
    T: IvoFieldValue,
    F: Fn(T, SharedIvoContext<I, O>, SharedRwCtxOptions<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 IntoRequiredErrorResolver<I: IvoSchemaStruct, O: IvoSchemaStruct, CtxOptions> {
    fn into_resolver(self) -> RequiredResolver<I, O, CtxOptions>;
}

impl<F, Fut, I: IvoSchemaStruct, O: IvoSchemaStruct, CtxOptions>
    IntoRequiredErrorResolver<I, O, CtxOptions> for F
where
    F: Fn(SharedIvoContext<I, O>, SharedRwCtxOptions<CtxOptions>) -> Fut + Send + Sync + 'static,
    Fut: Future<Output = String> + Send + 'static,
{
    fn into_resolver(self) -> RequiredResolver<I, O, CtxOptions> {
        Box::new(move |ctx, o| Box::pin(self(ctx, o).map(|e| (true, e))))
    }
}

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

impl<F, Fut, I: IvoSchemaStruct, O: IvoSchemaStruct, CtxOptions>
    IntoRequiredResolver<I, O, CtxOptions> for F
where
    F: Fn(SharedIvoContext<I, O>, SharedRwCtxOptions<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 IntoResolver<T, I: IvoSchemaStruct, O: IvoSchemaStruct, CtxOptions> {
    fn into_resolver(self) -> Resolver<T, I, O, CtxOptions>;
}

impl<F, Fut, T, I: IvoSchemaStruct, O: IvoSchemaStruct, CtxOptions>
    IntoResolver<T, I, O, CtxOptions> for F
where
    T: 'static,
    F: Fn(SharedIvoContext<I, O>, SharedRwCtxOptions<CtxOptions>) -> Fut + Send + Sync + 'static,
    Fut: Future<Output = T> + Send + 'static,
{
    fn into_resolver(self) -> Resolver<T, I, O, CtxOptions> {
        Box::new(move |ctx, o| Box::pin(self(ctx, o)))
    }
}

pub trait IntoUniformResolver<T, I: IvoSchemaStruct, O: IvoSchemaStruct, 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: IvoSchemaStruct,
    O: IvoSchemaStruct,
    T: IvoFieldValue,
    F: Fn(SharedIvoContext<I, O>, SharedRwCtxOptions<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 trait IntoResolverWithMiniContext<T, I: IvoSchemaStruct, CtxOptions> {
    fn into_uniform(self) -> UniformResolverWithMiniContext<I, CtxOptions>;
}

impl<F, Fut, T, I: IvoSchemaStruct, CtxOptions> IntoResolverWithMiniContext<T, I, CtxOptions> for F
where
    T: IvoFieldValue,
    F: Fn(SharedIvoMiniContext<I>, SharedRwCtxOptions<CtxOptions>) -> Fut + Send + Sync + 'static,
    Fut: Future<Output = T> + Send + 'static,
{
    fn into_uniform(self) -> UniformResolverWithMiniContext<I, CtxOptions> {
        Box::new(move |ctx, o| Box::pin(self(ctx, o).map(|v| erase_value(v))))
    }
}

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

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

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

pub type UniformResolverWithMiniContext<I, CtxOptions> = Box<
    dyn Fn(
            SharedIvoMiniContext<I>,
            SharedRwCtxOptions<CtxOptions>,
        ) -> BoxFuture<'static, ErasedValue>
        + Send
        + Sync
        + 'static,
>;

pub enum ComputableWithMiniContext<T, I: IvoSchemaStruct, CtxOptions> {
    Static(T),
    Func(UniformResolverWithMiniContext<I, CtxOptions>),
}

pub enum ComputableInit<I: IvoSchemaStruct, O: IvoSchemaStruct, CtxOptions> {
    False,
    Func(BooleanResolver<I, O, CtxOptions>),
}

pub enum ComputableRequiredError<I: IvoSchemaStruct, O: IvoSchemaStruct, CtxOptions> {
    Static(&'static str),
    Func(RequiredResolver<I, O, CtxOptions>),
}

pub type RequiredError = (bool, String);

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

pub type Resolver<T, I, O, CtxOptions> = Box<
    dyn Fn(SharedIvoContext<I, O>, SharedRwCtxOptions<CtxOptions>) -> BoxFuture<'static, T>
        + Send
        + Sync
        + 'static,
>;

pub type BooleanResolver<I, O, CtxOptions> = Resolver<bool, I, O, CtxOptions>;

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