supply 0.2.0

Provider API for arbitrary number of lifetimes.
Documentation
//! Main provide API.
//!
//! The initial design for a provide pattern was proposed in [RFC 3192](https://rust-lang.github.io/rfcs/3192-dyno.html).
//! It was proposed to provide [`Error`](core::error::Error) and API to expose extra context
//! information for errors. While the original RFC was a API general, the current implementation in
//! `core`/`std` is specifically for `Error`. The original proof of concept implementation can be
//! found at <https://github.com/nrc/provide-any>.
//!
//! [`supply`](crate) is a reimagining of the provide API to be more flexable and general purpose.
//! The motivation section of RFC 3192 states the problem this API is trying to solve very well.
//!
//! > However, in practice some kind of partial abstraction is required, where objects are treated
//! > abstractly but can be queried for data only present in a subset of all types which implement
//! > the trait interface. In this case there are only bad options: speculatively downcasting to
//! > concrete types (inefficient, boilerplatey, and fragile due to breaking abstraction) or adding
//! > numerous methods to the trait which might be functionally implemented, typically returning an
//! > Option where None means not applicable for the concrete type (boilerplatey, confusing, and
//! > leads to poor abstractions).
//!
//! <sub> <https://rust-lang.github.io/rfcs/3192-dyno.html#motivation> </sub>
//!
//! A similar pattern that is very useful but rarely seen in the wild is that proposed by
//! [`gdbstub`](https://docs.rs/gdbstub/latest/gdbstub/target/ext/index.html).
//! That of so called Inlineable Dyn Extension Traits (IDETs). You can think of the provide API
//! as an abstract form of this pattern.
//!
//! The core idea of the provide pattern is to use an output parameter to receive a type erased value.
//!
//! You may ask why just returning a type erased value is an issue, and its a good question. The
//! answer is that returning ownership directly requires something like a [`Box`]. However, we
//! don't always have access to a box or don't want the extra allocation. But wait what if we just
//! returned a `&dyn Any`? This doesn't need a box. However, this prevents returning owned values.
//! We are limited to things that can be borrowed from the source.
//!
//! Instead we take another approach. We construct a "hole". A place a value should go. We then
//! give a borrow of this hole to a value for it to "fill".
//!
//! This design has some major advantages.
//! For one it solves the returning ownership issue. Instead the provider transfers ownership into
//! the existing hole we gave it. Because as the requester we know the type of data we want we can
//! use some stack space to store the hole and eventual value.
//! Additionally, this design allows a requester to change the behavior of the hole. For example
//! we can have many holes that all need to be filled by the provider in one operation.
//!
//! Another aspect that `supply` expands on over the RFC is the use of arbitrary length lifetime
//! lists. We won't get into those here. See the [`lt_list`](crate::lt_list) module for more information about
//! those.
//!
//! Connecting this theoretical design to `supply`'s implementation we get the following.
//! The hole is represented by something implementing the [`Want`] trait. This trait has methods
//! for providing it a value to store. The provider then implements [`Provider`] and when it's
//! methods are called provides any values it can to the passed in want.
//!
//! A requester uses a provider by first constructing an empty [`Want`] implementer. Then, the
//! requester calls `.provide()` on the [`Provider`] to give the want a value. Then the requester
//! can remove the value from the want and use it however it needs to. This sequence is automated
//! by the [`request`](crate::request) module. Using [`ProviderExt`] we can use a simple method
//! call of the form `provider.request::<Request>()` to request values from a provider.

use crate::lt_list::{Lt1, LtList};
use crate::request::Request;
use crate::tag::{Reify, ReifySized};
use crate::want::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: LtList = Lt1<'r>>: Provider<'r, Lifetimes = L> {}

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

/// Provider of values.
///
/// Provided values may contain lifetimes from `L` and the `'r` lifetime.
pub trait Provider<'r> {
    /// Lifetimes the provider wants to use.
    ///
    /// A lifetime cannot be used in [`Self::provide`] or [`Self::provide_mut`]
    /// unless it is named in this associated type.
    type Lifetimes: LtList;

    /// Provide values given a `&T`.
    ///
    /// Values are provided to the passed in [`Want`].
    fn provide(&'r self, want: &mut dyn Want<Self::Lifetimes>) {
        _ = want;
    }

    /// Provide values given a `&mut T`.
    ///
    /// Values are provided to the passed in [`Want`].
    fn provide_mut(&'r mut self, want: &mut dyn Want<Self::Lifetimes>) {
        self.provide(want);
    }
}

pub trait ProviderExt<'r>: Provider<'r> {
    fn request<R>(&'r self) -> R::Output
    where
        R: Request<Self::Lifetimes>,
    {
        let mut want = R::Want::default();
        self.provide(&mut want);
        R::into_output(want)
    }

    fn request_mut<R>(&'r mut self) -> R::Output
    where
        R: Request<Self::Lifetimes>,
    {
        let mut want = R::Want::default();
        self.provide_mut(&mut want);
        R::into_output(want)
    }
}

impl<'r, T: ?Sized + Provider<'r>> ProviderExt<'r> for T {}

#[repr(transparent)]
pub struct ProvideTag<L: LtList, T: ?Sized + Reify<L>>(T::Reified);

impl<L: LtList, T: ?Sized + ReifySized<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: LtList, T: ?Sized + ReifySized<L>> core::fmt::Display for ProvideTag<L, T>
where
    T::Reified: core::fmt::Display,
{
    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
        core::fmt::Display::fmt(&self.0, f)
    }
}

impl<L: LtList, T: ?Sized + ReifySized<L>> ProvideTag<L, T> {
    pub fn from_ref(value: &T) -> &Self {
        unsafe { &*(value as *const T as *const Self) }
    }

    pub fn from_mut(value: &mut T) -> &mut Self {
        unsafe { &mut *(value as *mut T as *mut Self) }
    }
}

impl<'r, L: LtList, T: ?Sized + ReifySized<L>> Provider<'r> for ProvideTag<L, T> {
    type Lifetimes = Lt1<'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>) {
        want.provide_tag::<&mut T>(&mut self.0);
    }
}