use std::any::{Any, TypeId};
use std::collections::HashMap;
use std::fmt;
use crate::runtime::Never;
pub trait Service: Clone + 'static {
const NAME: &'static str;
#[inline]
fn to_context(self) -> ServiceContext
where
Self: Sized,
{
ServiceContext::empty().add(self)
}
#[inline]
fn layer(self) -> crate::layer::Layer<Self, Never, ()>
where
Self: Sized,
{
crate::layer::Layer::succeed(self)
}
#[inline]
fn use_<A, E, R, F>(f: F) -> crate::Effect<A, E, R>
where
Self: Sized,
A: 'static,
E: From<MissingService> + 'static,
R: ServiceLookup<Self> + 'static,
F: FnOnce(Self) -> crate::Effect<A, E, R> + 'static,
{
crate::Effect::<Self, E, R>::service::<Self>().flat_map(f)
}
#[inline]
fn use_sync<A, E, R, F>(f: F) -> crate::Effect<A, E, R>
where
Self: Sized,
A: 'static,
E: From<MissingService> + 'static,
R: ServiceLookup<Self> + 'static,
F: FnOnce(Self) -> A + 'static,
{
crate::Effect::<Self, E, R>::service::<Self>().map(f)
}
}
#[derive(Default)]
pub struct ServiceContext {
entries: HashMap<TypeId, ServiceEntry>,
}
struct ServiceEntry {
name: &'static str,
value: Box<dyn Any>,
clone_value: fn(&dyn Any) -> Box<dyn Any>,
}
impl Clone for ServiceEntry {
fn clone(&self) -> Self {
Self {
name: self.name,
value: (self.clone_value)(self.value.as_ref()),
clone_value: self.clone_value,
}
}
}
impl Clone for ServiceContext {
fn clone(&self) -> Self {
Self {
entries: self.entries.clone(),
}
}
}
impl fmt::Debug for ServiceContext {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut names = self
.entries
.values()
.map(|entry| entry.name)
.collect::<Vec<_>>();
names.sort_unstable();
f.debug_struct("ServiceContext")
.field("services", &names)
.finish()
}
}
impl ServiceContext {
#[inline]
pub fn empty() -> Self {
Self {
entries: HashMap::new(),
}
}
#[inline]
pub fn add<S>(mut self, service: S) -> Self
where
S: Service,
{
self.insert(service);
self
}
#[inline]
pub fn insert<S>(&mut self, service: S)
where
S: Service,
{
self.entries.insert(
TypeId::of::<S>(),
ServiceEntry {
name: S::NAME,
value: Box::new(service),
clone_value: |value| {
Box::new(
value
.downcast_ref::<S>()
.expect("service context entry stored under the wrong type")
.clone(),
)
},
},
);
}
#[inline]
pub fn merge(mut self, other: ServiceContext) -> Self {
self.entries.extend(other.entries);
self
}
#[inline]
pub fn get<S>(&self) -> Option<&S>
where
S: Service,
{
self
.entries
.get(&TypeId::of::<S>())
.and_then(|entry| entry.value.downcast_ref::<S>())
}
#[inline]
pub fn get_cloned<S>(&self) -> Option<S>
where
S: Service,
{
self.get::<S>().cloned()
}
#[inline]
pub fn contains<S>(&self) -> bool
where
S: Service,
{
self.entries.contains_key(&TypeId::of::<S>())
}
pub fn service_names(&self) -> Vec<&'static str> {
let mut names = self
.entries
.values()
.map(|entry| entry.name)
.collect::<Vec<_>>();
names.sort_unstable();
names
}
}
pub trait ServiceLookup<S: Service> {
fn service(&self) -> Option<&S>;
}
impl<S> ServiceLookup<S> for ServiceContext
where
S: Service,
{
#[inline]
fn service(&self) -> Option<&S> {
self.get::<S>()
}
}
impl<S> ServiceLookup<S> for &ServiceContext
where
S: Service,
{
#[inline]
fn service(&self) -> Option<&S> {
(*self).get::<S>()
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct MissingService {
pub name: &'static str,
}
impl fmt::Display for MissingService {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "missing service `{}`", self.name)
}
}
impl std::error::Error for MissingService {}