parval 0.1.0

Validation pattern compatible with concurrency models
Documentation
#![feature(try_trait_v2, const_destruct)]
use std::{
    marker::Destruct,
    ops::{ControlFlow, FromResidual, Try},
};

#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub struct Validation<T, E, W> {
    pub warnings: Vec<W>,
    pub result: Result<T, Vec<E>>,
}

pub struct ValidationResidual<E, W> {
    warnings: Vec<W>,
    errors: Vec<E>,
}

impl<T, E, W> Validation<T, E, W> {
    pub fn ok(self) -> Option<T> {
        self.result.ok()
    }

    pub fn map<U, F>(self, f: F) -> Validation<U, E, W>
    where
        F: FnOnce(T) -> U + Destruct,
    {
        Validation {
            warnings: self.warnings,
            result: self.result.map(f),
        }
    }

    pub fn warn(&mut self, warning: W) -> &mut Self {
        self.warnings.push(warning);
        self
    }

    pub fn warns<I>(&mut self, warnings: I) -> &mut Self
    where
        I: Iterator<Item = W>,
    {
        for warning in warnings {
            self.warnings.push(warning);
        }

        self
    }
}

impl<T, E, W> Try for Validation<T, E, W> {
    type Output = T;

    type Residual = ValidationResidual<E, W>;

    fn branch(self) -> std::ops::ControlFlow<Self::Residual, Self::Output> {
        match self.result {
            Err(errors) => ControlFlow::Break(ValidationResidual {
                warnings: self.warnings,
                errors,
            }),
            Ok(value) => ControlFlow::Continue(value),
        }
    }

    fn from_output(output: Self::Output) -> Self {
        Self {
            warnings: Vec::new(),
            result: Ok(output),
        }
    }
}

impl<T, E, W> FromResidual for Validation<T, E, W> {
    fn from_residual(residual: <Self as Try>::Residual) -> Self {
        Self {
            warnings: residual.warnings,
            result: Err(residual.errors),
        }
    }
}