#![allow(missing_docs, missing_debug_implementations)]
use std::any::TypeId;
mod tags {
use std::marker::PhantomData;
pub trait Type<'a>: Sized + 'static {
type Reified: 'a;
}
pub trait MaybeSizedType<'a>: Sized + 'static {
type Reified: 'a + ?Sized;
}
impl<'a, T: Type<'a>> MaybeSizedType<'a> for T {
type Reified = T::Reified;
}
#[derive(Debug)]
pub struct Value<T: 'static>(PhantomData<T>);
impl<T: 'static> Type<'_> for Value<T> {
type Reified = T;
}
#[derive(Debug)]
pub struct MaybeSizedValue<T: ?Sized + 'static>(PhantomData<T>);
impl<T: ?Sized + 'static> MaybeSizedType<'_> for MaybeSizedValue<T> {
type Reified = T;
}
#[derive(Debug)]
pub struct Ref<I>(PhantomData<I>);
impl<'a, I: MaybeSizedType<'a>> Type<'a> for Ref<I> {
type Reified = &'a I::Reified;
}
#[derive(Debug)]
pub struct RefMut<I>(PhantomData<I>);
impl<'a, I: MaybeSizedType<'a>> Type<'a> for RefMut<I> {
type Reified = &'a mut I::Reified;
}
}
pub trait Provider {
fn provide<'a>(&'a self, demand: &mut Demand<'a>);
fn provide_mut<'a>(&'a mut self, _demand: &mut Demand<'a>) {}
}
pub trait SlottedProvider {
fn provide<'a>(&'a self, slot: usize, demand: &mut Demand<'a>);
fn provide_mut<'a>(&'a mut self, _slot: usize, _demand: &mut Demand<'a>) {}
}
pub struct ProviderArray<const N: usize> {
pub providers: [Option<Box<dyn Provider>>; N],
}
impl<const N: usize> ProviderArray<N> {
pub fn set<T: 'static + Provider>(&mut self, slot: usize, provider: T) {
if slot < N {
self.providers[slot] = Some(Box::new(provider));
} else {
panic!("slot out of range");
}
}
}
impl<const N: usize> Default for ProviderArray<N> {
fn default() -> Self {
const NONE: Option<Box<dyn Provider>> = None;
Self {
providers: [NONE; N],
}
}
}
impl<const N: usize> SlottedProvider for ProviderArray<N> {
fn provide<'a>(&'a self, slot: usize, demand: &mut Demand<'a>) {
if slot < N {
if let Some(provider) = &self.providers[slot] {
provider.provide(demand);
}
}
}
fn provide_mut<'a>(&'a mut self, slot: usize, demand: &mut Demand<'a>) {
if slot < N {
if let Some(provider) = &mut self.providers[slot] {
provider.provide_mut(demand);
}
}
}
}
#[repr(transparent)]
pub struct Demand<'a>(dyn Erased<'a> + 'a);
pub struct Receiver<'a, 'b, I: tags::Type<'a>>(&'b mut TaggedOption<'a, I>);
impl<'a, I: tags::Type<'a>> Receiver<'a, '_, I> {
#[inline]
pub fn provide(&mut self, value: I::Reified) {
self.0 .0 = Some(value)
}
}
impl<'a> Demand<'a> {
fn new<'b>(erased: &'b mut (dyn Erased<'a> + 'a)) -> &'b mut Demand<'a> {
unsafe { &mut *(erased as *mut dyn Erased<'a> as *mut Demand<'a>) }
}
pub fn provide_value<T, F>(&mut self, fulfil: F) -> &mut Self
where
T: 'static,
F: FnOnce() -> T,
{
self.provide_with::<tags::Value<T>, F>(fulfil)
}
pub fn provide_ref<T: ?Sized + 'static>(&mut self, value: &'a T) -> &mut Self {
self.provide::<tags::Ref<tags::MaybeSizedValue<T>>>(value)
}
pub fn provide_mut<T: ?Sized + 'static>(&mut self, value: &'a mut T) -> &mut Self {
self.provide::<tags::RefMut<tags::MaybeSizedValue<T>>>(value)
}
pub fn maybe_provide_mut<T: ?Sized + 'static>(
&mut self,
) -> Option<Receiver<'a, '_, tags::RefMut<tags::MaybeSizedValue<T>>>> {
self.0
.downcast_mut::<tags::RefMut<tags::MaybeSizedValue<T>>>()
.map(Receiver)
}
fn provide<I>(&mut self, value: I::Reified) -> &mut Self
where
I: tags::Type<'a>,
{
if let Some(res @ TaggedOption(None)) = self.0.downcast_mut::<I>() {
res.0 = Some(value);
}
self
}
fn provide_with<I, F>(&mut self, fulfil: F) -> &mut Self
where
I: tags::Type<'a>,
F: FnOnce() -> I::Reified,
{
if let Some(res @ TaggedOption(None)) = self.0.downcast_mut::<I>() {
res.0 = Some(fulfil());
}
self
}
}
impl std::fmt::Debug for Demand<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Demand").finish_non_exhaustive()
}
}
unsafe trait Erased<'a>: 'a {
fn tag_id(&self) -> TypeId;
}
unsafe impl<'a, I: tags::Type<'a>> Erased<'a> for TaggedOption<'a, I> {
fn tag_id(&self) -> TypeId {
TypeId::of::<I>()
}
}
impl<'a> dyn Erased<'a> + 'a {
#[inline]
fn downcast_mut<I>(&mut self) -> Option<&mut TaggedOption<'a, I>>
where
I: tags::Type<'a>,
{
if self.tag_id() == TypeId::of::<I>() {
Some(unsafe { &mut *(self as *mut Self).cast::<TaggedOption<'a, I>>() })
} else {
None
}
}
}
#[repr(transparent)]
struct TaggedOption<'a, I: tags::Type<'a>>(Option<I::Reified>);
impl<'a, I: tags::Type<'a>> TaggedOption<'a, I> {
fn as_demand(&mut self) -> &mut Demand<'a> {
Demand::new(self as &mut (dyn Erased<'a> + 'a))
}
}
pub fn request_ref<'a, T, P>(provider: &'a P) -> Option<&'a T>
where
T: 'static + ?Sized,
P: Provider + ?Sized,
{
request_by_type_tag::<'a, tags::Ref<tags::MaybeSizedValue<T>>, P>(provider)
}
pub fn request_ref_by_slot<'a, T, P>(provider: &'a P, slot: usize) -> Option<&'a T>
where
T: 'static + ?Sized,
P: SlottedProvider + ?Sized,
{
let mut tagged = TaggedOption::<'a, tags::Ref<tags::MaybeSizedValue<T>>>(None);
provider.provide(slot, tagged.as_demand());
tagged.0
}
pub fn request_mut<'a, T, P>(provider: &'a mut P) -> Option<&'a mut T>
where
T: 'static + ?Sized,
P: Provider + ?Sized,
{
let mut tagged = TaggedOption::<'a, tags::RefMut<tags::MaybeSizedValue<T>>>(None);
provider.provide_mut(tagged.as_demand());
tagged.0
}
fn request_by_type_tag<'a, I, P>(provider: &'a P) -> Option<I::Reified>
where
I: tags::Type<'a>,
P: Provider + ?Sized,
{
let mut tagged = TaggedOption::<'a, I>(None);
provider.provide(tagged.as_demand());
tagged.0
}
impl Provider for () {
fn provide<'a>(&'a self, demand: &mut Demand<'a>) {
demand.provide_ref(self);
}
fn provide_mut<'a>(&'a mut self, demand: &mut Demand<'a>) {
demand.provide_mut(self);
}
}