supply 0.3.0

Provider API for arbitrary number of lifetimes.
Documentation
//! Extra [`Provider`] related types and traits.

use ty_tag::lifetime_list::{LifetimeList, L1};
use ty_tag::Reify;

use crate::{Provider, Want};

/// Helper for using [`Provider`] as a trait object.
///
/// Normally using [`Provider`] as a trait object would require
/// always naming the lifetime associated type with `Lifetimes = ...`.
/// This helper makes it just a generic.
pub trait ProviderDyn<'r, L: LifetimeList = L1<'r>>: Provider<'r, Lifetimes = L> {}

impl<'r, L: LifetimeList, T: ?Sized + Provider<'r, Lifetimes = L>> ProviderDyn<'r, L> for T {}

/// [`Provider`] for a single value.
///
/// This type provides either a `&T::Reified` or `&mut T::Reified`. The value is provided using tags
/// `&T` or `&mut T`.
#[repr(transparent)]
pub struct ProvideTag<L: LifetimeList, T: ?Sized + Reify<L>>(pub T::Reified);

impl<L: LifetimeList, T: ?Sized + Reify<L>> core::fmt::Debug for ProvideTag<L, T>
where
    T::Reified: core::fmt::Debug,
{
    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
        f.debug_tuple("ProvideTag").field(&&self.0).finish()
    }
}

impl<L: LifetimeList, T: ?Sized + Reify<L>> ProvideTag<L, T> {
    pub const fn from_ref(value: &T::Reified) -> &Self {
        // SAFETY: Self is repr transparent to the reified type.
        unsafe { &*(core::ptr::from_ref(value) as *const Self) }
    }

    pub const fn from_mut(value: &mut T::Reified) -> &mut Self {
        // SAFETY: Self is repr transparent to the reified type.
        unsafe { &mut *(core::ptr::from_mut(value) as *mut Self) }
    }
}

impl<'r, L: LifetimeList, T: ?Sized + Reify<L>> Provider<'r> for ProvideTag<L, T> {
    type Lifetimes = L1<'r, L>;

    fn provide(&'r self, want: &mut dyn Want<Self::Lifetimes>) {
        want.provide_tag::<&T>(&self.0);
    }

    fn provide_mut(&'r mut self, want: &mut dyn Want<Self::Lifetimes>) {
        let Some(this) = want.provide_with::<&mut T, _, _>(self, |this| &mut this.0) else {
            return;
        };

        this.provide(want);
    }
}