#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
pub enum ServiceScope {
#[default]
Singleton,
Transient,
Scoped,
}
pub type ServiceLifetime = ServiceScope;
impl ServiceScope {
pub fn is_singleton(&self) -> bool {
matches!(self, ServiceScope::Singleton)
}
pub fn is_transient(&self) -> bool {
matches!(self, ServiceScope::Transient)
}
pub fn is_scoped(&self) -> bool {
matches!(self, ServiceScope::Scoped)
}
pub fn as_str(&self) -> &'static str {
match self {
ServiceScope::Singleton => "singleton",
ServiceScope::Transient => "transient",
ServiceScope::Scoped => "scoped",
}
}
}
impl std::fmt::Display for ServiceScope {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.as_str())
}
}
impl std::str::FromStr for ServiceScope {
type Err = crate::errors::CoreError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s.to_lowercase().as_str() {
"singleton" => Ok(ServiceScope::Singleton),
"transient" => Ok(ServiceScope::Transient),
"scoped" => Ok(ServiceScope::Scoped),
_ => Err(crate::errors::CoreError::InvalidServiceScope {
scope: s.to_string(),
}),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct ScopeId(uuid::Uuid);
impl ScopeId {
pub fn new() -> Self {
Self(uuid::Uuid::new_v4())
}
pub fn as_uuid(&self) -> uuid::Uuid {
self.0
}
}
impl Default for ScopeId {
fn default() -> Self {
Self::new()
}
}
impl std::fmt::Display for ScopeId {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.0)
}
}
#[derive(Debug)]
pub struct ScopedServiceManager {
scope_id: ScopeId,
services: std::sync::RwLock<
std::collections::HashMap<std::any::TypeId, Box<dyn std::any::Any + Send + Sync>>,
>,
parent: Option<std::sync::Arc<ScopedServiceManager>>,
}
impl ScopedServiceManager {
pub fn new() -> Self {
Self {
scope_id: ScopeId::new(),
services: std::sync::RwLock::new(std::collections::HashMap::new()),
parent: None,
}
}
pub fn create_child(parent: std::sync::Arc<Self>) -> Self {
Self {
scope_id: ScopeId::new(),
services: std::sync::RwLock::new(std::collections::HashMap::new()),
parent: Some(parent),
}
}
pub fn scope_id(&self) -> &ScopeId {
&self.scope_id
}
pub fn add_service<T>(&self, service: T)
where
T: Send + Sync + 'static,
{
let type_id = std::any::TypeId::of::<T>();
let mut services = self.services.write().unwrap();
services.insert(type_id, Box::new(service));
}
pub fn add_arc_service<T>(&self, service: std::sync::Arc<T>)
where
T: Send + Sync + 'static,
{
let type_id = std::any::TypeId::of::<T>();
let mut services = self.services.write().unwrap();
services.insert(type_id, Box::new(service));
}
pub fn get_arc_service<T>(&self) -> Option<std::sync::Arc<T>>
where
T: Send + Sync + 'static,
{
let type_id = std::any::TypeId::of::<T>();
{
let services = self.services.read().unwrap();
if let Some(service) = services.get(&type_id) {
if let Some(arc) = service.downcast_ref::<std::sync::Arc<T>>() {
return Some(arc.clone());
}
}
}
self.parent.as_ref()?.get_arc_service::<T>()
}
pub fn has_service<T>(&self) -> bool
where
T: Send + Sync + 'static,
{
let type_id = std::any::TypeId::of::<T>();
{
let services = self.services.read().unwrap();
if services.contains_key(&type_id) {
return true;
}
}
self.parent.as_ref().is_some_and(|p| p.has_service::<T>())
}
pub fn has_service_local<T>(&self) -> bool
where
T: Send + Sync + 'static,
{
let type_id = std::any::TypeId::of::<T>();
let services = self.services.read().unwrap();
services.contains_key(&type_id)
}
pub fn clear(&self) {
let mut services = self.services.write().unwrap();
services.clear();
}
pub fn service_count(&self) -> usize {
let services = self.services.read().unwrap();
services.len()
}
pub fn parent(&self) -> Option<&std::sync::Arc<ScopedServiceManager>> {
self.parent.as_ref()
}
}
impl Default for ScopedServiceManager {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_service_scope_from_str() {
assert_eq!(
"singleton".parse::<ServiceScope>().unwrap(),
ServiceScope::Singleton
);
assert_eq!(
"transient".parse::<ServiceScope>().unwrap(),
ServiceScope::Transient
);
assert_eq!(
"scoped".parse::<ServiceScope>().unwrap(),
ServiceScope::Scoped
);
assert!("invalid".parse::<ServiceScope>().is_err());
}
#[test]
fn test_service_scope_display() {
assert_eq!(format!("{}", ServiceScope::Singleton), "singleton");
assert_eq!(format!("{}", ServiceScope::Transient), "transient");
assert_eq!(format!("{}", ServiceScope::Scoped), "scoped");
}
#[test]
fn test_scoped_service_manager() {
let manager = ScopedServiceManager::new();
manager.add_arc_service(std::sync::Arc::new("test_string".to_string()));
manager.add_arc_service(std::sync::Arc::new(42u32));
assert!(manager.has_service::<String>());
assert!(manager.has_service::<u32>());
assert!(!manager.has_service::<i32>());
assert_eq!(
*manager.get_arc_service::<String>().unwrap(),
"test_string".to_string()
);
assert_eq!(*manager.get_arc_service::<u32>().unwrap(), 42u32);
assert_eq!(manager.service_count(), 2);
manager.clear();
assert_eq!(manager.service_count(), 0);
}
#[test]
fn test_scope_inheritance() {
let parent = std::sync::Arc::new(ScopedServiceManager::new());
parent.add_arc_service(std::sync::Arc::new("parent_string".to_string()));
parent.add_arc_service(std::sync::Arc::new(100u32));
let child = std::sync::Arc::new(ScopedServiceManager::create_child(parent.clone()));
child.add_arc_service(std::sync::Arc::new(200u32));
assert_eq!(*child.get_arc_service::<u32>().unwrap(), 200u32);
assert_eq!(
*child.get_arc_service::<String>().unwrap(),
"parent_string".to_string()
);
assert_eq!(*parent.get_arc_service::<u32>().unwrap(), 100u32);
assert_eq!(parent.service_count(), 2);
assert_eq!(child.service_count(), 1);
assert!(child.has_service::<String>()); assert!(child.has_service::<u32>()); assert!(!child.has_service_local::<String>()); assert!(child.has_service_local::<u32>()); }
}