use std::any::{type_name, TypeId};
use std::fmt::{Display, Formatter};
use std::sync::Arc;
use dashmap::DashMap;
use log::{debug, trace, warn};
use crate::core::{DynBean, Error};
use crate::core::bean_def::BeanDef;
use crate::core::ty::Type;
#[derive(Clone)]
pub struct Context {
inner: Arc<InnerContext>,
}
struct InnerContext {
name: String,
beans: Arc<DashMap<String, DynBean>>,
bean_defs: Arc<DashMap<String, Arc<BeanDef>>>,
contexts: Arc<DashMap<String, Arc<Context>>>,
}
impl Context {
pub fn new(name: &str) -> Context {
Context {
inner: Arc::new(InnerContext {
name: name.to_string(),
beans: Default::default(),
bean_defs: Default::default(),
contexts: Default::default(),
})
}
}
pub fn name(&self) -> &str {
&self.inner.name
}
pub fn add_context(&self, context: Context) {
self.inner.contexts.insert(context.name().to_string(), Arc::new(context));
}
pub fn register(&self, bean_def: impl Into<BeanDef>) -> Result<(), Error> {
let bean_def = bean_def.into();
if let Some(_) = self.inner.get_bean_def(bean_def.name()) {
warn!("failed to register duplicated BeanDef(name={}, type={}) in {}", bean_def.name(), bean_def.ty().name(), self);
return Err(Error::from(format!("failed to register duplicated BeanDef(name={}, type={}) in {}", bean_def.name(), bean_def.ty().name(), self)));
};
trace!("registering {} within {}", &bean_def, self);
self.inner.bean_defs.insert(bean_def.name().to_string(), Arc::new(bean_def));
Ok(())
}
pub fn get_bean<T: ?Sized + 'static>(&self, name: &str) -> Result<Arc<T>, Error> {
if let Some(dyn_bean) = self.inner.get_bean(name) {
return Type::downcast::<T>(dyn_bean);
}
let Some(bean_def) = self.inner.get_bean_def(name) else {
warn!("cannot resolve Bean(name={}, type={}) in {}", name, type_name::<T>(), self);
return Err(Error::from(format!("cannot resolve Bean(name={}, type={}) in {}", name, type_name::<T>(), self)));
};
let (name, dyn_bean) = bean_def.get(self)?;
if let Some(_) = self.inner.beans.insert(name.clone(), dyn_bean.clone()) {
warn!("unexpected duplicated bean has been created Bean(name={}, type={}) in {}", &name, bean_def.ty().name(), self);
return Err(Error::from(format!("unexpected duplicated bean has been created Bean(name={}, type={}) in {}", name, bean_def.ty().name(), self)));
};
debug!("Bean(name={}, type={}) has been added to {}", &name, bean_def.ty().name(), self);
return Type::downcast::<T>(dyn_bean);
}
pub fn get_primary_bean<T: ?Sized + 'static>(&self) -> Result<Arc<T>, Error> {
let type_id = TypeId::of::<T>();
let mut candidates = self.inner.get_bean_defs_by_type(self, &type_id);
match candidates.len() {
0 => {
Err(Error::from(""))
},
1 => {
let bean_def = candidates.pop().unwrap();
self.get_bean::<T>(bean_def.name())
},
_ => {
todo!("missed feature(primary beans) - add primary to BeanDef and use it to resolve primary bean")
},
}
}
pub fn get_beans<T: ?Sized + 'static>(&self) -> Result<Vec<Arc<T>>, Error> {
let type_id = TypeId::of::<T>();
self.inner.get_bean_defs_by_type(self, &type_id)
.iter()
.map(|def| self.get_bean::<T>(def.name()))
.collect()
}
pub async fn init_beans(&self) -> Result<(), Error> {
Ok(())
}
}
impl Display for Context {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "Context(name={})", &self.inner.name)
}
}
impl InnerContext {
fn get_bean(&self, name: &str) -> Option<DynBean> {
self.beans.get(name).map(|dyn_ref| dyn_ref.value().clone())
}
fn get_bean_def(&self, name: &str) -> Option<Arc<BeanDef>> {
if let Some(bean_def) = self.bean_defs.get(name) {
trace!("found {} in Context(name={})", bean_def.value(), &self.name);
return Some(bean_def.value().clone());
}
for ctx in self.contexts.iter() {
if let Some(bean_def) = ctx.inner.get_bean_def(name) {
return Some(bean_def);
}
}
trace!("cannot find BeanDef(name={}) in Context(name={})", name, &self.name);
return None;
}
fn get_bean_defs_within_context(&self, ctx: &Context) -> Vec<Arc<BeanDef>> {
let mut bean_defs = Vec::new();
let mut ctx_defs = self.bean_defs.iter()
.map(|def| def.value().clone())
.collect();
bean_defs.append(&mut ctx_defs);
for child_ctx in self.contexts.iter() {
let mut ctx_defs = child_ctx.inner.get_bean_defs_within_context(ctx);
bean_defs.append(&mut ctx_defs);
}
return bean_defs;
}
fn get_bean_defs_by_type(&self, ctx: &Context, type_id: &TypeId) -> Vec<Arc<BeanDef>> {
self.get_bean_defs_within_context(ctx).into_iter()
.filter(|def| def.ty().assignable(type_id))
.collect()
}
}
#[cfg(test)]
mod tests {
use std::sync::Arc;
use crate::context::context::Context;
use crate::core::bean_def::BeanDef;
use crate::core::Error;
use crate::core::ty::Type;
struct TestBean { name: &'static str, }
trait TestTrait { fn name(&self) -> &'static str; }
impl TestTrait for TestBean {
fn name(&self) -> &'static str { self.name }
}
#[test]
fn register_types_bean_def_then_should_return_and_cast_to_struct_and_dyn_trait() -> Result<(), Error>{
let ctx = Context::new("test-context");
let ty = Type::of::<TestBean>();
ty.add_downcast::<TestBean>(|b| Ok(Arc::downcast::<TestBean>(b)?));
ty.add_downcast::<dyn TestTrait + Sync + Send>(|b| Ok(Arc::downcast::<TestBean>(b)?));
let bean_def = BeanDef::builder()
.ty(ty)
.name("testBean")
.get(Arc::new(|_ctx| Ok(Arc::new(TestBean { name: "instance_of_testBean" }))))
.build();
ctx.register(bean_def)?;
let bean = ctx.get_bean::<TestBean>("testBean")?;
assert_eq!(bean.as_ref().name, "instance_of_testBean");
let bean = ctx.get_bean::<dyn TestTrait + Sync + Send>("testBean")?;
assert_eq!(bean.name(), "instance_of_testBean");
Ok(())
}
struct TestBeanWithDep {
dyn_dep: Arc<dyn TestTrait + Sync + Send>
}
#[test]
fn should_create_and_get_bean_with_dyn_dep() -> Result<(), Error>{
let ctx = Context::new("test-context");
let ty = Type::of::<TestBean>();
ty.add_downcast::<TestBean>(|b| Ok(Arc::downcast::<TestBean>(b)?));
ty.add_downcast::<dyn TestTrait + Sync + Send>(|b| Ok(Arc::downcast::<TestBean>(b)?));
let bean_def = BeanDef::builder()
.ty(ty)
.name("testBean")
.get(Arc::new(|_ctx| Ok(Arc::new(TestBean { name: "instance_of_testBean" }))))
.build();
ctx.register(bean_def)?;
let ty = Type::of::<TestBeanWithDep>();
ty.add_downcast::<TestBeanWithDep>(|b| Ok(Arc::downcast::<TestBeanWithDep>(b)?));
let bean_def = BeanDef::builder()
.ty(ty)
.name("testBeanWithDep")
.get(Arc::new(|ctx| {
let bean = Arc::new(TestBeanWithDep {
dyn_dep: ctx.get_bean("testBean")?,
});
Ok(bean)
}))
.build();
ctx.register(bean_def)?;
let bean_dep = ctx.get_bean::<TestBean>("testBean")?;
assert_eq!(bean_dep.name, "instance_of_testBean");
let bean = ctx.get_bean::<TestBeanWithDep>("testBeanWithDep")?;
assert_eq!(bean.dyn_dep.name(), "instance_of_testBean");
Ok(())
}
}