supply 0.2.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::lt_list::{Lt1, LtList};
use crate::tag::{ReifySized, TagTypeId};
use crate::want::{ErasedWantFor, Want, WantOne};

/// A request that can be made of a [`Provider`].
pub trait Request<L: LtList> {
    /// The want that will be passed to the provider.
    type Want: Want<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<L: LtList, T: ReifySized<L>> Request<L> for T {
    type Want = WantOne<L, T::UsedTag>;

    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<Lt1<'r, L>> for $name<$($type),*>
        where
            $($type: Request<Lt1<'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<$($type),*> Request<$crate::lt_list::Nil> for $name<$($type),*>
        where
            $($type: Request<$crate::lt_list::Nil>,)*
        {
            // 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<L: LtList $(, $type)*> Want<L> for $name<$($type),*>
        where
            $($type: Want<L>,)*
        {
            #[allow(non_snake_case)]
            fn try_for_id(
                &mut self,
                tag_type_id: TagTypeId<L>,
            ) -> Option<ErasedWantFor<'_, 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);