supply 0.1.0

Provider API for arbitrary number of lifetimes.
Documentation
//! Request for values from a provider.
//!
//! These are an extension of wants that know how to
//! generate an output value to give to the caller.
//! All types that implement [`Tagged`](crate::tag::Tagged) also implement [`Request`].
//! This allows requesting single values using `<type>::request(provider)`.
//!
//! This module provides up to six multi-value requests. Additionally
//! if this limit is not enough, then the `Req` types can be nested inside
//! each other.

use crate::tag::{ReifySized, TagTypeId};
use crate::want::{ErasedWantFor, WantOne};
use crate::{Lt1, LtList, Provider, Want};

pub trait RequestExt {
    fn request<'r, P>(provider: &'r P) -> Self::Output
    where
        Self: Request<'r, P::Lifetimes>,
        P: ?Sized + Provider,
    {
        let mut want = Self::Want::default();
        provider.provide(&mut want);
        Self::into_output(want)
    }

    fn request_mut<'r, P>(provider: &'r mut P) -> Self::Output
    where
        Self: Request<'r, P::Lifetimes>,
        P: ?Sized + Provider,
    {
        let mut want = Self::Want::default();
        provider.provide_mut(&mut want);
        Self::into_output(want)
    }
}

pub fn request_from<'r, T, P>(provider: &'r P) -> T::Output
where
    T: Request<'r, P::Lifetimes>,
    P: ?Sized + Provider,
{
    T::request(provider)
}

pub fn request_from_mut<'r, T, P>(provider: &'r mut P) -> T::Output
where
    T: Request<'r, P::Lifetimes>,
    P: ?Sized + Provider,
{
    T::request_mut(provider)
}

impl<T> RequestExt for T {}

/// A request that can be made of a [`Provider`].
pub trait Request<'r, L: LtList> {
    /// The want that will be passed to the provider.
    type Want: Want<'r, L> + Default;

    /// The output value of the request.
    type Output;

    /// Convert the want into an output.
    fn into_output(want: Self::Want) -> Self::Output;
}

// All tagged types automatically become requests.
// The request is for one value of the tag's reified type.
impl<'r, L: LtList, T: ReifySized<Lt1<'r, L>>> Request<'r, L> for T {
    type Want = WantOne<'r, L, T::Tag>;

    type Output = Option<T::Reified>;

    fn into_output(want: Self::Want) -> Self::Output {
        want.into_inner()
    }
}

macro_rules! impl_reqx {
    ($name:ident: $($type:ident),*) => {
        /// Combine multiple requests into one.
        ///
        /// This allows requesting multiple values at the same time.
        #[derive(Default, Debug)]
        pub struct $name<$($type),*>($(pub $type),*);

        impl<'r, L: LtList $(, $type)*> Request<'r, L> for $name<$($type),*>
        where
            $($type: Request<'r, L>,)*
        {
            // Acts as it's own want that knows how to do both.
            type Want = $name<$($type::Want),*>;

            // Acts as final storage as well. We don't use a tuple because those are tagged.
            type Output = $name<$($type::Output),*>;

            #[allow(non_snake_case)]
            fn into_output(want: Self::Want) -> Self::Output {
                let $name($($type),*) = want;

                $name($($type::into_output($type)),*)
            }
        }

        impl<'r, L: LtList $(, $type)*> Want<'r, L> for $name<$($type),*>
        where
            $($type: Want<'r, L>,)*
        {
            #[allow(non_snake_case)]
            fn try_for_id(
                &mut self,
                tag_type_id: TagTypeId<Lt1<'r, L>>,
            ) -> Option<ErasedWantFor<'_, Lt1<'r, L>>> {
                let $name($($type),*) = self;

                $(if !$type.is_satisfied() {
                    if let Some(want) = $type.try_for_id(tag_type_id) {
                        return Some(want);
                    }
                })*

                None
            }

            #[allow(non_snake_case)]
            fn is_satisfied(&self) -> bool {
                let $name($($type),*) = self;

                $($type.is_satisfied())||*
            }
        }
    };
}

impl_reqx!(Req2: T0, T1);
impl_reqx!(Req3: T0, T1, T2);
impl_reqx!(Req4: T0, T1, T2, T3);
impl_reqx!(Req5: T0, T1, T2, T3, T4);
impl_reqx!(Req6: T0, T1, T2, T3, T4, T5);