use crate::container::descriptor::ServiceId;
use crate::errors::CoreError;
use std::any::Any;
use std::sync::Arc;
pub trait Injectable: Send + Sync + 'static {
fn dependencies() -> Vec<ServiceId>;
fn create<R: DependencyResolver>(resolver: &R) -> Result<Self, CoreError>
where
Self: Sized;
}
pub trait DependencyResolver {
fn resolve<T: Send + Sync + 'static>(&self) -> Result<Arc<T>, CoreError>;
fn resolve_named<T: Send + Sync + 'static>(&self, name: &str) -> Result<Arc<T>, CoreError>;
fn try_resolve<T: Send + Sync + 'static>(&self) -> Option<Arc<T>>;
fn try_resolve_named<T: Send + Sync + 'static>(&self, name: &str) -> Option<Arc<T>>;
}
pub trait AutowireService: Send + Sync + 'static {
type ServiceType: Injectable;
fn autowire<R: DependencyResolver>(resolver: &R) -> Result<Self::ServiceType, CoreError>;
}
pub trait ConstructorParameter: Send + Sync + 'static {
fn service_id() -> ServiceId;
fn resolve<R: DependencyResolver>(resolver: &R) -> Result<Self, CoreError>
where
Self: Sized;
}
impl<T: Send + Sync + 'static> ConstructorParameter for Arc<T> {
fn service_id() -> ServiceId {
ServiceId::of::<T>()
}
fn resolve<R: DependencyResolver>(resolver: &R) -> Result<Self, CoreError> {
resolver.resolve::<T>()
}
}
impl<T: Send + Sync + 'static> ConstructorParameter for Option<Arc<T>> {
fn service_id() -> ServiceId {
ServiceId::of::<T>()
}
fn resolve<R: DependencyResolver>(resolver: &R) -> Result<Self, CoreError> {
Ok(resolver.try_resolve::<T>())
}
}
#[derive(Debug, Clone)]
pub struct ParameterInfo {
pub type_name: &'static str,
pub service_id: ServiceId,
pub is_optional: bool,
}
#[derive(Debug, Clone)]
pub struct ConstructorInfo {
pub service_type: &'static str,
pub parameters: Vec<ParameterInfo>,
}
pub trait ConstructorMetadata {
fn constructor_info() -> ConstructorInfo;
}
pub trait InjectableFactory<T: Send + Sync + 'static> {
fn create_instance<R: DependencyResolver>(resolver: &R) -> Result<T, CoreError>;
fn dependencies() -> Vec<ServiceId>;
}
pub trait InjectableHelper {
fn extract_dependencies() -> Vec<ServiceId>;
fn create_with_resolver<R: DependencyResolver>(
resolver: &R,
) -> Result<Box<dyn Any + Send + Sync>, CoreError>;
}
impl Injectable for () {
fn dependencies() -> Vec<ServiceId> {
Vec::new()
}
fn create<R: DependencyResolver>(_resolver: &R) -> Result<Self, CoreError> {
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::collections::HashMap;
struct MockResolver {
services: HashMap<ServiceId, Box<dyn Any + Send + Sync>>,
}
impl MockResolver {
fn new() -> Self {
Self {
services: HashMap::new(),
}
}
fn register<T: Send + Sync + 'static>(&mut self, instance: T) {
let service_id = ServiceId::of::<T>();
self.services
.insert(service_id, Box::new(Arc::new(instance)));
}
#[allow(dead_code)]
fn register_trait<T: ?Sized + Send + Sync + 'static>(&mut self, instance: Arc<T>) {
let service_id = ServiceId::of::<T>();
self.services.insert(service_id, Box::new(instance));
}
}
impl DependencyResolver for MockResolver {
fn resolve<T: Send + Sync + 'static>(&self) -> Result<Arc<T>, CoreError> {
let service_id = ServiceId::of::<T>();
let instance =
self.services
.get(&service_id)
.ok_or_else(|| CoreError::ServiceNotFound {
service_type: std::any::type_name::<T>().to_string(),
})?;
let arc_instance =
instance
.downcast_ref::<Arc<T>>()
.ok_or_else(|| CoreError::ServiceNotFound {
service_type: std::any::type_name::<T>().to_string(),
})?;
Ok(arc_instance.clone())
}
fn resolve_named<T: Send + Sync + 'static>(
&self,
_name: &str,
) -> Result<Arc<T>, CoreError> {
self.resolve::<T>()
}
fn try_resolve<T: Send + Sync + 'static>(&self) -> Option<Arc<T>> {
self.resolve::<T>().ok()
}
fn try_resolve_named<T: Send + Sync + 'static>(&self, name: &str) -> Option<Arc<T>> {
self.resolve_named::<T>(name).ok()
}
}
#[allow(dead_code)]
trait Repository: Send + Sync {
fn find(&self, id: u32) -> Option<String>;
}
#[allow(dead_code)]
trait EmailService: Send + Sync {
fn send(&self, to: &str, subject: &str, body: &str) -> Result<(), String>;
}
#[allow(dead_code)]
trait Logger: Send + Sync {
fn log(&self, message: &str);
}
#[derive(Default)]
struct PostgresRepository;
impl Repository for PostgresRepository {
fn find(&self, _id: u32) -> Option<String> {
Some("user data".to_string())
}
}
#[derive(Default)]
struct SmtpEmailService;
impl EmailService for SmtpEmailService {
fn send(&self, _to: &str, _subject: &str, _body: &str) -> Result<(), String> {
Ok(())
}
}
#[derive(Default)]
struct FileLogger;
impl Logger for FileLogger {
fn log(&self, _message: &str) {
}
}
struct UserService {
repository: Arc<PostgresRepository>,
email_service: Arc<SmtpEmailService>,
logger: Option<Arc<FileLogger>>, }
impl UserService {
pub fn new(
repository: Arc<PostgresRepository>,
email_service: Arc<SmtpEmailService>,
logger: Option<Arc<FileLogger>>,
) -> Self {
Self {
repository,
email_service,
logger,
}
}
pub fn create_user(&self, name: &str) -> Result<String, String> {
if let Some(logger) = &self.logger {
logger.log(&format!("Creating user: {}", name));
}
let _existing = self.repository.find(1);
let user_id = format!("user_{}", name);
self.email_service.send(
&format!("{}@example.com", name),
"Welcome",
"Welcome to our service",
)?;
Ok(user_id)
}
}
impl Injectable for UserService {
fn dependencies() -> Vec<ServiceId> {
vec![
ServiceId::of::<PostgresRepository>(),
ServiceId::of::<SmtpEmailService>(),
ServiceId::of::<FileLogger>(), ]
}
fn create<R: DependencyResolver>(resolver: &R) -> Result<Self, CoreError> {
let repository = resolver.resolve::<PostgresRepository>()?;
let email_service = resolver.resolve::<SmtpEmailService>()?;
let logger = resolver.try_resolve::<FileLogger>();
Ok(UserService::new(repository, email_service, logger))
}
}
#[test]
fn test_parameter_type_extraction() {
assert_eq!(Arc::<String>::service_id(), ServiceId::of::<String>());
assert_eq!(
Option::<Arc<String>>::service_id(),
ServiceId::of::<String>()
);
}
#[test]
fn test_injectable_with_dependencies() {
let mut resolver = MockResolver::new();
resolver.register(PostgresRepository);
resolver.register(SmtpEmailService);
resolver.register(FileLogger);
let user_service = UserService::create(&resolver).unwrap();
let result = user_service.create_user("john");
assert!(result.is_ok());
assert_eq!(result.unwrap(), "user_john");
}
#[test]
fn test_optional_dependencies() {
let mut resolver = MockResolver::new();
resolver.register(PostgresRepository);
resolver.register(SmtpEmailService);
let user_service = UserService::create(&resolver).unwrap();
let result = user_service.create_user("jane");
assert!(result.is_ok());
}
#[test]
fn test_missing_required_dependency() {
let mut resolver = MockResolver::new();
resolver.register(PostgresRepository);
let result = UserService::create(&resolver);
assert!(result.is_err());
if let Err(CoreError::ServiceNotFound { .. }) = result {
} else {
panic!("Expected ServiceNotFound error");
}
}
#[test]
fn test_dependency_list() {
let deps = UserService::dependencies();
assert_eq!(deps.len(), 3);
assert!(deps.contains(&ServiceId::of::<PostgresRepository>()));
assert!(deps.contains(&ServiceId::of::<SmtpEmailService>()));
assert!(deps.contains(&ServiceId::of::<FileLogger>()));
}
}