supply 0.3.1

Provider API for arbitrary number of lifetimes.
Documentation
//! Wants to be given to a provider to fulfill.

mod want_one;

mod erased;

pub use erased::ErasedWantFor;
use ty_tag::lifetime_list::LifetimeList;
use ty_tag::{ReifySelf, ReifySized, TagTypeId};
pub use want_one::WantOne;

use crate::Want;

/// A want for a `T`.
pub trait WantFor<T> {
    /// Fulfill the want with a value.
    ///
    /// Calling this when [`WantFor::is_satisfied`] returns true may replace the value or keep the
    /// old one. It is recommended to not call this in such cases to prevent unusual behavior.
    fn fulfill(&mut self, value: T);

    /// Checks if the want for `T` is fully satisfied.
    ///
    /// Once satisfied, no more values of type `T` need to be provided.
    /// This may return a `true` when the `dyn Want` this is derived from does not.
    /// In those cases the want doesn't need any more `T` but can accept values of other types.
    fn is_satisfied(&self) -> bool {
        false
    }
}

// Extra non-object safe methods.
impl<L: LifetimeList> dyn Want<L> + '_ {
    /// Provide a value using it's [`ReifySelf`] tag.
    ///
    /// Not all values implement [`ReifySelf`], and you may want to use a custom tag
    /// instead of the value's normal tag. In those cases use `Self::provide_tag`.
    ///
    /// This method can be chained with other provide methods.
    pub fn provide_value<T: ReifySelf<L>>(&mut self, value: T) -> &mut Self {
        if let Some(want) = self.try_for::<T>() {
            want.fulfill(value);
        }

        self
    }

    /// Provide a tagged value to the want.
    ///
    /// `T` can be any [`ReifySized`] implementing type.
    ///
    /// This method can be chained with other provide methods.
    ///
    /// If `value` is expensive to create, use `Self::provide`.
    pub fn provide_tag<T: ?Sized + ReifySized<L>>(&mut self, value: T::Reified) -> &mut Self {
        if let Some(want) = self.try_for::<T>() {
            want.fulfill(value);
        }

        self
    }

    /// Provide a tagged value to the want.
    ///
    /// `T` can be any [`ReifySized`] implementing type.
    ///
    /// This method can be chained with other provide methods.
    ///
    /// The value is given as the result of a function. This allows
    /// not generating the value if the want does not need the value.
    /// This should be used for values that are expensive to create.
    ///
    /// If you need to pass in a context into `f`, use `Self::provide_with`.
    pub fn provide<T, F>(&mut self, f: F) -> &mut Self
    where
        T: ReifySized<L>,
        F: FnOnce() -> <T as ReifySized<L>>::SizedReified,
    {
        if let Some(want) = self.try_for::<T>() {
            want.fulfill(f());
        }

        self
    }

    /// Provide a tagged value to the want.
    ///
    /// `T` can be any [`ReifySized`] implementing type.
    ///
    /// This provide method does not allow chaining as it returns the context
    /// value if the want does not want a `T`. Instead the next provide call will
    /// need to start a new chain.
    ///
    /// The value is given as the result of a function. This allows
    /// not generating the value if the want does not need the value.
    /// This should be used for values that are expensive to create.
    ///
    /// If you don't need a context, use `Self::provide`.
    pub fn provide_with<T, C, F>(&mut self, ctx: C, f: F) -> Option<C>
    where
        T: ReifySized<L>,
        F: FnOnce(C) -> <T as ReifySized<L>>::SizedReified,
    {
        if let Some(want) = self.try_for::<T>() {
            want.fulfill(f(ctx));
            None
        } else {
            Some(ctx)
        }
    }

    /// Wrapper of [`Want::try_for_id`] that takes a tag instead of a [`TagTypeId`].
    ///
    /// This method should only be used if one of the provide methods doesn't work.
    pub fn try_for<T: ?Sized + ReifySized<L>>(&mut self) -> Option<&mut dyn WantFor<T::Reified>> {
        // Call try_for_id with the id of the T passed in.
        let erased = self.try_for_id(TagTypeId::<L>::of_reify::<T>())?;

        // If the want gave a bad WantFor we just ignore it.
        erased.downcast::<T>().ok()
    }
}