use crate::config::GlobalConfig;
use crate::definition::{InjectionBean, InjectionBeanDefinition};
use crate::utils::json_merge_patch;
use crate::{Component, Configure};
use std::any::{Any, TypeId};
#[cfg(feature = "config")]
use std::collections::HashMap;
use std::sync::Arc;
use tokio::sync::OnceCell;
pub type InstanceKey = (TypeId, &'static str);
pub(crate) static CONTAINER: OnceCell<Arc<InjectionContainer>> = OnceCell::const_new();
pub struct InjectionContainer {
registry: HashMap<InstanceKey, Arc<InjectionBean>>,
config: GlobalConfig,
}
impl InjectionContainer {
fn new() -> Self {
let mut registry = HashMap::new();
for definition in inventory::iter::<InjectionBeanDefinition> {
registry.insert(
(definition.type_id, definition.name),
Arc::new(InjectionBean::new(definition.init_fn)),
);
}
Self {
registry,
config: GlobalConfig::new(),
}
}
pub async fn init<F: FnOnce()>(init_fn: impl Into<Option<F>>) {
let container = CONTAINER
.get_or_init(|| async { Arc::new(InjectionContainer::new()) })
.await;
if let Some(f) =init_fn.into(){
f();
}
container.prewarm_all().await;
}
pub fn instance() -> &'static Arc<InjectionContainer> {
CONTAINER
.get()
.expect("Container not initialized. Call InjectionContainer::init() first in async context.")
}
#[cfg(all(feature = "config", feature = "serde"))]
pub fn get_config<T: Configure>() -> T {
Self::get_config_by_key(T::prefix())
}
#[cfg(all(feature = "config", feature = "serde"))]
pub fn get_config_by_key<T: Configure>(key: &str) -> T {
let default = T::default();
if let Ok( config) = Self::instance().config.get(key){
if let Ok(mut default_value) = serde_json::to_value(&default) && let Ok(config_value) = serde_json::to_value(&config){
json_merge_patch(&mut default_value, &config_value);
if let Ok(merge_value) = serde_json::from_value(default_value) {
return merge_value;
}
}
config
}else{
default
}
}
async fn get_named_component_async<T: Component>(&self, name: &str) -> &'static T {
let entry = self.get_injection_bean(TypeId::of::<T>(), name);
let injection_instance = entry.get().await;
injection_instance
.downcast_ref()
.expect("Bean can not downcast.")
}
fn get_injection_bean<'a>(&'a self, type_id: TypeId, name: &'a str) -> &'a Arc<InjectionBean>{
self
.registry
.get(&(type_id, name))
.expect(&format!(
"No dependency entry found for with name {}",
name
))
}
fn get_named_component<T: Component>(&self, name: &str) -> &'static T {
let entry = self.get_injection_bean(TypeId::of::<T>(), name);
let injection_instance = entry.get_inner();
injection_instance
.downcast_ref()
.expect("Bean can not downcast.")
}
pub fn get<T: Component>() -> &'static T {
Self::instance().resolve()
}
pub async fn get_async<T: Component>() -> &'static T {
Self::instance().resolve_async().await
}
fn resolve<T: Component>(&self) -> &'static T {
self.get_named_component(T::component_name())
}
async fn resolve_async<T: Component>(&self) -> &'static T {
self.get_named_component_async(T::component_name()).await
}
async fn prewarm_all(&self) {
for (key, value) in self.registry.iter() {
let value_clone = value.clone();
let key_name = key.1;
tokio::spawn(async move {
log::debug!("Prewarming bean start: {}", key_name);
let _ = value_clone.get().await;
log::debug!("Prewarming bean end: {}", key_name);
});
}
}
}
impl Default for InjectionContainer {
fn default() -> Self {
Self::new()
}
}
pub trait ArcAnyExt {
fn downcast_arc<T: Any + Send + Sync>(self: Arc<Self>) -> Option<Arc<T>>
where
Self: Send + Sync + 'static;
}
impl ArcAnyExt for dyn Any + Send + Sync {
fn downcast_arc<T: Any + Send + Sync>(self: Arc<Self>) -> Option<Arc<T>>
where
Self: Send + Sync + 'static,
{
if self.is::<T>() {
let raw = Arc::into_raw(self) as *const T;
Some(unsafe { Arc::from_raw(raw) })
} else {
None
}
}
}