supply 0.3.1

Provider API for arbitrary number of lifetimes.
Documentation
//! Extra requests.
//!
//! All types implementing [`Tagged`](ty_tag::Tagged) can be used as a request.
//! This module provides some extra combinations of requests.

use ty_tag::lifetime_list::LifetimeList;

use super::{Request, Want};
use crate::want::ErasedWantFor;

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),*>($($type),*);

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

            type Output = ($($type::Output),*);

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

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

        impl<L: LifetimeList $(, $type)*> Want<L> for $name<$($type),*>
        where
            $($type: Want<L>,)*
        {
            #[allow(non_snake_case)]
            fn try_for_id(
                &mut self,
                tag_type_id: ty_tag::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())&&*
            }
        }
    };
}

/// Trait for [`Option`].
///
/// This is used by the `ReqAllN` types.
///
/// This trait is sealed and cannot be implemented.
pub trait Optional: optional_seal::Sealed {
    type Value;

    fn into_option(self) -> Option<Self::Value>;
}

impl<T> Optional for Option<T> {
    type Value = T;

    fn into_option(self) -> Option<Self::Value> {
        self
    }
}

mod optional_seal {
    pub trait Sealed {}

    impl<T> Sealed for Option<T> {}
}

macro_rules! impl_reqx_all {
    ($name:ident: $($type:ident),*) => {
        /// Request a value of each type.
        ///
        /// If any type's value isn't given then the request returns `None`.
        #[derive(Default, Debug)]
        pub struct $name<$($type),*>($($type),*);

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

            type Output = Option<($(<$type::Output as Optional>::Value),*)>;

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

                $(
                let $type = $type::into_output($type).into_option();
                )*

                if let ($(Some($type)),*) = ($($type),*) {
                    Some(($($type),*))
                } else {
                    None
                }
            }
        }

        impl<L: LifetimeList $(, $type)*> Want<L> for $name<$($type),*>
        where
            $($type: Want<L>,)*
        {
            #[allow(non_snake_case)]
            fn try_for_id(
                &mut self,
                tag_type_id: ty_tag::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);

impl_reqx_all!(ReqAll2: T0, T1);
impl_reqx_all!(ReqAll3: T0, T1, T2);
impl_reqx_all!(ReqAll4: T0, T1, T2, T3);
impl_reqx_all!(ReqAll5: T0, T1, T2, T3, T4);
impl_reqx_all!(ReqAll6: T0, T1, T2, T3, T4, T5);