use crate::config::{AppConfig, DatabaseConnection};
use parking_lot::RwLock;
use std::any::{Any, TypeId};
use std::collections::HashMap;
use std::sync::{Arc, OnceLock};
type ServiceInstance = Arc<dyn Any + Send + Sync>;
type ScopeInstances = HashMap<TypeId, ServiceInstance>;
static APP: OnceLock<App> = OnceLock::new();
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Lifetime {
Singleton,
Transient,
Scoped,
}
pub type ScopeId = u64;
type ServiceFactory = Arc<dyn Fn() -> Box<dyn Any + Send + Sync> + Send + Sync>;
#[derive(Clone)]
struct ServiceBinding {
factory: ServiceFactory,
lifetime: Lifetime,
}
pub struct Container {
bindings: RwLock<HashMap<TypeId, ServiceBinding>>,
instances: RwLock<HashMap<TypeId, ServiceInstance>>,
scoped_instances: RwLock<HashMap<ScopeId, ScopeInstances>>,
aliases: RwLock<HashMap<String, TypeId>>,
}
impl Container {
pub fn new() -> Self {
Self {
bindings: RwLock::new(HashMap::new()),
instances: RwLock::new(HashMap::new()),
scoped_instances: RwLock::new(HashMap::new()),
aliases: RwLock::new(HashMap::new()),
}
}
pub fn bind<T, F>(&self, factory: F)
where
T: Send + Sync + 'static,
F: Fn() -> T + Send + Sync + 'static,
{
let type_id = TypeId::of::<T>();
let binding = ServiceBinding {
factory: Arc::new(move || Box::new(factory())),
lifetime: Lifetime::Transient,
};
self.bindings.write().insert(type_id, binding);
}
pub fn singleton<T, F>(&self, factory: F)
where
T: Send + Sync + 'static,
F: Fn() -> T + Send + Sync + 'static,
{
let type_id = TypeId::of::<T>();
let binding = ServiceBinding {
factory: Arc::new(move || Box::new(factory())),
lifetime: Lifetime::Singleton,
};
self.bindings.write().insert(type_id, binding);
}
pub fn scoped<T, F>(&self, factory: F)
where
T: Send + Sync + 'static,
F: Fn() -> T + Send + Sync + 'static,
{
let type_id = TypeId::of::<T>();
let binding = ServiceBinding {
factory: Arc::new(move || Box::new(factory())),
lifetime: Lifetime::Scoped,
};
self.bindings.write().insert(type_id, binding);
}
pub fn instance<T>(&self, instance: T)
where
T: Send + Sync + 'static,
{
let type_id = TypeId::of::<T>();
let arc: Arc<dyn Any + Send + Sync> = Arc::new(instance);
self.instances.write().insert(type_id, arc);
self.bindings.write().insert(
type_id,
ServiceBinding {
factory: Arc::new(|| {
panic!("instance() 绑定的服务不应调用工厂 — 这是内部不变量违反")
}),
lifetime: Lifetime::Singleton,
},
);
}
pub fn alias<T: 'static>(&self, name: impl Into<String>) {
let type_id = TypeId::of::<T>();
self.aliases.write().insert(name.into(), type_id);
}
pub fn resolve_alias(&self, name: &str) -> Option<TypeId> {
self.aliases.read().get(name).copied()
}
pub fn is_alias(&self, name: &str) -> bool {
self.aliases.read().contains_key(name)
}
pub fn debug_aliases(&self) -> Vec<String> {
self.aliases.read().keys().cloned().collect()
}
pub fn make<T: Send + Sync + 'static>(&self) -> Option<Arc<T>> {
self.make_with_scope::<T>(0)
}
pub fn make_with_scope<T: Send + Sync + 'static>(&self, scope_id: ScopeId) -> Option<Arc<T>> {
let type_id = TypeId::of::<T>();
if let Some(cached) = self.instances.read().get(&type_id) {
return Arc::downcast::<T>(cached.clone()).ok();
}
if scope_id != 0 {
let scoped = self.scoped_instances.read();
if let Some(scope_map) = scoped.get(&scope_id) {
if let Some(cached) = scope_map.get(&type_id) {
return Arc::downcast::<T>(cached.clone()).ok();
}
}
}
let guard = self.bindings.read();
let binding = guard.get(&type_id)?.clone();
drop(guard);
let instance = (binding.factory)();
match binding.lifetime {
Lifetime::Singleton => {
let arc: Arc<dyn Any + Send + Sync> = Arc::from(instance);
self.instances.write().insert(type_id, arc.clone());
Arc::downcast::<T>(arc).ok()
}
Lifetime::Scoped => {
let arc: Arc<dyn Any + Send + Sync> = Arc::from(instance);
self.scoped_instances
.write()
.entry(scope_id)
.or_default()
.insert(type_id, arc.clone());
Arc::downcast::<T>(arc).ok()
}
Lifetime::Transient => {
Arc::downcast::<T>(Arc::from(instance)).ok()
}
}
}
pub fn clear_scope(&self, scope_id: ScopeId) {
self.scoped_instances.write().remove(&scope_id);
}
pub fn has<T: 'static>(&self) -> bool {
let type_id = TypeId::of::<T>();
self.bindings.read().contains_key(&type_id)
}
pub fn forget<T: 'static>(&self) {
let type_id = TypeId::of::<T>();
self.bindings.write().remove(&type_id);
self.instances.write().remove(&type_id);
let mut scoped = self.scoped_instances.write();
for scope_map in scoped.values_mut() {
scope_map.remove(&type_id);
}
}
pub fn clear(&self) {
self.bindings.write().clear();
self.instances.write().clear();
self.scoped_instances.write().clear();
self.aliases.write().clear();
}
pub fn count(&self) -> usize {
self.bindings.read().len()
}
pub fn alias_count(&self) -> usize {
self.aliases.read().len()
}
pub fn active_scope_count(&self) -> usize {
self.scoped_instances.read().len()
}
}
impl Default for Container {
fn default() -> Self {
Self::new()
}
}
impl std::fmt::Debug for Container {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Container")
.field("bindings_count", &self.bindings.read().len())
.field("instances_count", &self.instances.read().len())
.field("scoped_scope_count", &self.scoped_instances.read().len())
.field("aliases_count", &self.aliases.read().len())
.finish()
}
}
pub struct App {
config: AppConfig,
db_connections: HashMap<String, DatabaseConnection>,
cache: RwLock<Option<String>>,
log: RwLock<Option<String>>,
container: Container,
}
impl App {
pub fn new(config: AppConfig) -> App {
let db_connections = config.database.connections.clone();
App {
config,
db_connections,
cache: RwLock::new(None),
log: RwLock::new(None),
container: Container::new(),
}
}
pub fn init(config: AppConfig) -> &'static App {
APP.get_or_init(|| App::new(config))
}
pub fn global() -> Option<&'static App> {
APP.get()
}
pub fn config(&self) -> &AppConfig {
&self.config
}
pub fn db_connection(&self, name: &str) -> Option<&DatabaseConnection> {
self.db_connections.get(name)
}
pub fn db_connection_names(&self) -> Vec<&str> {
self.db_connections.keys().map(|s| s.as_str()).collect()
}
pub fn default_db_connection(&self) -> Option<&DatabaseConnection> {
self.db_connection(&self.config.database.default)
}
pub fn set_cache(&self, cache: impl Into<String>) {
let mut guard = self.cache.write();
*guard = Some(cache.into());
}
pub fn cache(&self) -> Option<String> {
self.cache.read().clone()
}
pub fn set_log(&self, log: impl Into<String>) {
let mut guard = self.log.write();
*guard = Some(log.into());
}
pub fn log(&self) -> Option<String> {
self.log.read().clone()
}
pub fn container(&self) -> &Container {
&self.container
}
pub fn bind<T, F>(&self, factory: F)
where
T: Send + Sync + 'static,
F: Fn() -> T + Send + Sync + 'static,
{
self.container.bind(factory);
}
pub fn singleton<T, F>(&self, factory: F)
where
T: Send + Sync + 'static,
F: Fn() -> T + Send + Sync + 'static,
{
self.container.singleton(factory);
}
pub fn scoped<T, F>(&self, factory: F)
where
T: Send + Sync + 'static,
F: Fn() -> T + Send + Sync + 'static,
{
self.container.scoped(factory);
}
pub fn instance<T>(&self, instance: T)
where
T: Send + Sync + 'static,
{
self.container.instance(instance);
}
pub fn alias<T: 'static>(&self, name: impl Into<String>) {
self.container.alias::<T>(name);
}
pub fn make<T: Send + Sync + 'static>(&self) -> Option<Arc<T>> {
self.container.make::<T>()
}
pub fn make_with_scope<T: Send + Sync + 'static>(&self, scope_id: ScopeId) -> Option<Arc<T>> {
self.container.make_with_scope::<T>(scope_id)
}
pub fn clear_scope(&self, scope_id: ScopeId) {
self.container.clear_scope(scope_id);
}
pub fn has_service<T: 'static>(&self) -> bool {
self.container.has::<T>()
}
}
impl std::fmt::Debug for App {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("App")
.field("config", &self.config)
.field(
"db_connections",
&self.db_connections.keys().collect::<Vec<_>>(),
)
.field("cache", &self.cache.read().is_some())
.field("log", &self.log.read().is_some())
.field("container", &self.container)
.finish()
}
}
#[cfg(test)]
mod tests;