#![allow(dead_code)]
use crate::{helpers::service_container, service::Service, Handler, Injectable, Singleton};
use std::{
any::{Any, TypeId},
collections::HashMap,
sync::{Arc, OnceLock, RwLock},
};
pub(crate) static SERVICE_CONTAINER: OnceLock<Arc<ServiceContainer>> = OnceLock::new();
pub struct ServiceContainer {
services: RwLock<HashMap<TypeId, Box<dyn Any + Send + Sync + 'static>>>,
in_proxy_mode: bool,
}
impl Default for ServiceContainer {
fn default() -> Self {
Self::new()
}
}
impl ServiceContainer {
pub(crate) fn new() -> Self {
Self {
services: RwLock::new(HashMap::new()),
in_proxy_mode: false,
}
}
pub fn proxy() -> Self {
Self {
services: RwLock::new(HashMap::new()),
in_proxy_mode: true,
}
}
pub fn is_proxy(&self) -> bool {
self.in_proxy_mode
}
pub fn proxy_value<T: Clone + 'static>(&self) -> Option<T> {
if self.is_proxy() {
self.get_type::<T>()
} else {
None
}
}
pub fn get<T: 'static>(&self) -> Option<Service<T>> {
self.get_type::<Service<T>>()
}
pub async fn get_or_inject<T: Injectable + Send + Sync + 'static>(&self) -> Service<T> {
let result = self.get::<T>();
if result.is_none() {
let instance = T::inject(self).await;
return self.set(instance).get::<T>().unwrap();
}
result.unwrap()
}
pub async fn get_type_or_inject<T: Injectable + Clone + Send + Sync + 'static>(&self) -> T {
let result = self.get_type::<T>();
if result.is_none() {
let instance = T::inject(self).await;
self.set_type(instance.clone());
return instance;
}
result.unwrap()
}
pub fn get_type<T: Clone + 'static>(&self) -> Option<T> {
if let Ok(services) = self.services.read() {
let result: Option<&T> = services
.get(&TypeId::of::<T>())
.and_then(|b| b.downcast_ref());
if let Some(service) = result {
return Some(service.clone());
} else if self.is_proxy() {
return service_container().get_type();
}
}
None
}
pub fn set_type<T: Clone + Send + Sync + 'static>(&self, ext: T) -> &Self {
if let Ok(mut list) = self.services.write() {
list.insert(TypeId::of::<T>(), Box::new(ext));
}
self
}
pub fn set<T: Send + Sync + 'static>(&self, ext: T) -> &Self {
self.set_type(Service::new(ext))
}
pub async fn inject_and_call<F, Args>(&self, handler: F) -> F::Output
where
F: Handler<Args>,
Args: Injectable + 'static,
{
let args = Args::inject(self).await;
handler.call(args).await
}
pub async fn inject_all<Args>(&self) -> Args
where
Args: Injectable + 'static,
{
Args::inject(self).await
}
pub async fn provide<T: Injectable + Send + Sync + 'static>(&self) -> T {
T::inject(self).await
}
pub async fn service<T: 'static>(&self) -> Service<T> {
Service::inject(self).await
}
pub async fn singleton<T: Injectable + Sized + Send + Sync + 'static>(&self) -> Singleton<T> {
Singleton::inject(self).await
}
}
pub struct ServiceContainerBuilder {
items: HashMap<TypeId, Box<dyn Any + Send + Sync + 'static>>,
}
impl Default for ServiceContainerBuilder {
fn default() -> Self {
Self::new()
}
}
impl ServiceContainerBuilder {
pub fn new() -> Self {
Self {
items: HashMap::new(),
}
}
pub fn register<T: Clone + Send + Sync + 'static>(mut self, ext: T) -> Self {
self.items.insert(TypeId::of::<T>(), Box::new(ext));
self
}
pub fn service<T: Send + Sync + 'static>(mut self, ext: T) -> Self {
self.items
.insert(TypeId::of::<Service<T>>(), Box::new(Service::new(ext)));
self
}
pub fn build(self) -> Arc<ServiceContainer> {
let container = SERVICE_CONTAINER.get_or_init(|| Arc::new(ServiceContainer::default()));
if let Ok(mut services) = container.services.write() {
for (k, v) in self.items {
services.insert(k, v);
}
}
container.clone()
}
}
#[cfg(test)]
mod test {
use async_trait::async_trait;
use super::*;
#[derive(Debug, Clone)]
struct Counter {
start_point: usize,
}
#[async_trait]
impl Injectable for Counter {
async fn inject(container: &ServiceContainer) -> Self {
container
.get_type()
.unwrap_or_else(|| Counter { start_point: 44 })
}
}
#[derive(Debug, Clone)]
struct User {
id: i32,
}
#[async_trait]
impl Injectable for User {
async fn inject(_: &ServiceContainer) -> Self {
Self { id: 1000 }
}
}
#[tokio::test]
async fn test_builder() {
let container = ServiceContainerBuilder::new()
.service(5usize)
.register(true)
.build();
assert_eq!(*container.get::<usize>().unwrap(), 5usize);
assert_eq!(container.get_type::<bool>(), Some(true));
}
#[tokio::test]
async fn test_empty_container() {
let container = ServiceContainer::new();
assert_eq!(container.get::<i32>().is_none(), true);
assert_eq!(container.get_type::<i32>(), None);
}
#[tokio::test]
async fn test_getting_raw_type() {
let container = ServiceContainer::new();
container.set_type(400);
container.set_type(300f32);
container.set_type(true);
assert_eq!(container.get_type::<i32>(), Some(400));
assert_eq!(container.get_type::<f32>(), Some(300f32));
assert_eq!(container.get_type::<bool>(), Some(true));
}
#[tokio::test]
async fn test_getting_service_type() {
let container = ServiceContainer::new();
container.set(400);
container.set(300f32);
container.set(true);
assert_eq!(*container.get::<i32>().unwrap(), 400);
assert_eq!(*container.get::<f32>().unwrap(), 300f32);
assert_eq!(*container.get::<bool>().unwrap(), true);
}
#[tokio::test]
async fn test_proxy_service() {
service_container().set_type(true);
let container = ServiceContainer::proxy();
let is_true: Option<bool> = container.get_type();
let an_i32: Option<i32> = container.get_type();
assert_eq!(is_true, Some(true));
assert_eq!(an_i32, None);
container.set_type(30000);
let rate_per_hour: Option<i32> = container.get_type();
assert_eq!(rate_per_hour, Some(30000));
}
#[tokio::test]
async fn test_injecting() {
let container = ServiceContainer::new();
let counter = container.inject_all::<Counter>().await;
assert_eq!(counter.start_point, 44usize);
}
#[tokio::test]
async fn test_injecting_stored_instance() {
let container = ServiceContainer::new();
container.set_type(Counter { start_point: 6000 });
let counter = container.inject_all::<Counter>().await;
assert_eq!(counter.start_point, 6000usize);
}
#[tokio::test]
async fn test_singleton() {
let container = ServiceContainer::new();
let user = container.singleton::<User>().await;
assert_eq!(user.id, 1000);
container.set_type(User { id: 88 });
let user = container.singleton::<User>().await;
assert_eq!(user.id, 1000);
}
#[tokio::test]
async fn test_inject_and_call() {
let container = ServiceContainer::new();
let result = container
.inject_and_call(|user: User, counter: Counter| async move {
assert_eq!(user.id, 1000);
assert_eq!(counter.start_point, 44);
(1, 2, 3)
})
.await;
assert_eq!(result, (1, 2, 3));
}
#[tokio::test]
async fn test_get_or_inject_raw_type() {
let container = ServiceContainer::new();
assert_eq!(container.get_type::<User>().is_none(), true);
let a_user = container.get_type_or_inject::<User>().await;
let a_user2 = container.get_type::<User>();
assert_eq!(a_user.id, 1000);
assert_eq!(a_user2.is_some(), true);
assert_eq!(a_user2.unwrap().id, a_user.id);
}
#[tokio::test]
async fn test_get_or_inject_service_type() {
let container = ServiceContainer::new();
assert_eq!(container.get::<User>().is_none(), true);
let a_user = container.get_or_inject::<User>().await;
let a_user2 = container.get::<User>();
assert_eq!(a_user.id, 1000);
assert_eq!(a_user2.is_some(), true);
assert_eq!(a_user2.unwrap().id, a_user.id);
}
}