flow-di 0.1.0

A dependency injection framework for Rust inspired by C# AutoFac and Microsoft.Extensions.DependencyInjection
Documentation
//! # Flow-DI
//!
//! A Rust dependency injection framework inspired by C# AutoFac and Microsoft.Extensions.DependencyInjection
//!
//! ## Features
//!
//! - **Three Lifetime Management Types**: Singleton, Scoped, Transient
//! - **Keyed Service Support**: Named services and key-based service resolution
//! - **Automatic Dependency Injection**: Automatic dependency injection when creating service instances
//! - **Fluent API**: Easy-to-use service registration API using builder pattern
//! - **Circular Dependency Detection**: Automatic detection and prevention of circular dependencies
//! - **Thread Safety**: Full support for multi-threaded environments
//! - **Scope Management**: Nested scopes and automatic resource cleanup
//!
//! ## Quick Start
//!
//! ```rust
//! use flow_di::{ContainerBuilder, ServiceProviderExt};
//! use std::sync::Arc;
//!
//! // Define service interface
//! trait IRepository: Send + Sync {
//!     fn get_data(&self) -> String;
//! }
//!
//! // Implement service
//! #[derive(Debug)]
//! struct DatabaseRepository {
//!     connection_string: String,
//! }
//!
//! impl IRepository for DatabaseRepository {
//!     fn get_data(&self) -> String {
//!         format!("Data from database: {}", self.connection_string)
//!     }
//! }
//!
//! // Configure and build container
//! let provider = ContainerBuilder::new()
//!     .add_instance("localhost:5432".to_string()) // Register configuration
//!     .add_singleton_with_deps::<DatabaseRepository, DatabaseRepository, String>(
//!         |connection_string| DatabaseRepository {
//!             connection_string: (*connection_string).clone(),
//!         }
//!     )
//!     .build();
//!
//! // Resolve service
//! let repository = provider.get_required_service::<DatabaseRepository>().unwrap();
//! println!("{}", repository.get_data());
//! ```

pub mod builder;
pub mod container;
pub mod descriptor;
pub mod errors;
pub mod lifetime;
pub mod service_key;

// 重新导出核心类型
pub use builder::ContainerBuilder;
pub use container::{Container, ServiceProvider, ServiceScope};
pub use descriptor::{
    ServiceDescriptor, ServiceFactory, ServiceProvider as ServiceProviderTrait, ServiceProviderExt,
};
pub use errors::{DiError, DiResult};
pub use lifetime::Lifetime;
pub use service_key::ServiceKey;

/// Convenient function to create a container builder
///
/// This is a shortcut for `ContainerBuilder::new()`.
///
/// # Example
///
/// ```rust
/// use flow_di::{container_builder, ServiceProviderExt};
///
/// let provider = container_builder()
///     .add_instance(42i32)
///     .add_transient_simple::<String, String>(|| "hello".to_string())
///     .build();
///
/// let number = provider.get_required_service::<i32>().unwrap();
/// assert_eq!(*number, 42);
/// ```
pub fn container_builder() -> ContainerBuilder {
    ContainerBuilder::new()
}

/// Convenient function to create a container
///
/// This is a shortcut for `Container::new()`.
///
/// # Example
///
/// ```rust
/// use flow_di::{container, ServiceDescriptor};
///
/// let container = container();
/// let descriptor = ServiceDescriptor::from_instance(42i32);
/// container.register(descriptor).unwrap();
/// ```
pub fn container() -> Container {
    Container::new()
}

#[cfg(test)]
mod integration_tests {
    use super::*;
    use crate::descriptor::ServiceProviderExt;
    use std::sync::Arc;

    trait ILogger: Send + Sync {
        fn log(&self, message: &str);
        fn get_logs(&self) -> Vec<String>;
    }

    struct ConsoleLogger {
        logs: std::sync::Mutex<Vec<String>>,
    }

    impl ConsoleLogger {
        fn new() -> Self {
            Self {
                logs: std::sync::Mutex::new(Vec::new()),
            }
        }
    }

    impl ILogger for ConsoleLogger {
        fn log(&self, message: &str) {
            if let Ok(mut logs) = self.logs.lock() {
                logs.push(message.to_string());
            }
        }

        fn get_logs(&self) -> Vec<String> {
            self.logs.lock().unwrap().clone()
        }
    }

    struct DatabaseService {
        logger: Arc<ConsoleLogger>,
        connection_string: Arc<String>,
    }

    impl DatabaseService {
        fn connect(&self) -> String {
            self.logger.log("Connecting to database");
            format!("Connected to {}", self.connection_string)
        }
    }

    struct UserService {
        database: Arc<DatabaseService>,
        logger: Arc<ConsoleLogger>,
    }

    impl UserService {
        fn get_user(&self, id: i32) -> String {
            self.logger.log(&format!("Getting user {id}"));
            let _ = self.database.connect();
            format!("User {id}")
        }
    }

    #[test]
    fn test_complete_dependency_injection_scenario() {
        let provider = ContainerBuilder::new()
            // 注册配置
            .add_instance("postgresql://localhost:5432/mydb".to_string())
            // 注册日志服务(单例)
            .add_singleton_simple::<ConsoleLogger, ConsoleLogger>(ConsoleLogger::new)
            // 注册数据库服务(作用域,依赖日志和配置)
            .add_scoped_with_deps2::<DatabaseService, DatabaseService, ConsoleLogger, String>(
                |logger, connection_string| DatabaseService {
                    logger,
                    connection_string,
                },
            )
            // 注册用户服务(瞬时,依赖数据库和日志)
            .add_transient_with_deps2::<UserService, UserService, DatabaseService, ConsoleLogger>(
                |database, logger| UserService { database, logger },
            )
            .build();

        // 创建作用域并测试服务解析
        let mut scope = provider.create_scope().unwrap();

        // 获取用户服务
        let user_service = scope.get_required_service::<UserService>().unwrap();
        let result = user_service.get_user(123);

        assert_eq!(result, "User 123");

        // 验证日志记录
        let logger = scope.get_required_service::<ConsoleLogger>().unwrap();
        let logs = logger.get_logs();
        assert!(logs.contains(&"Getting user 123".to_string()));
        assert!(logs.contains(&"Connecting to database".to_string()));

        // 测试作用域内的服务实例共享
        let db1 = scope.get_required_service::<DatabaseService>().unwrap();
        let db2 = scope.get_required_service::<DatabaseService>().unwrap();
        // 验证服务被正确解析
        assert_eq!(db1.connection_string, db2.connection_string);

        // 测试瞬时服务
        let user1 = scope.get_required_service::<UserService>().unwrap();
        let user2 = scope.get_required_service::<UserService>().unwrap();
        // 验证服务被正确创建
        assert_eq!(
            user1.database.connection_string,
            user2.database.connection_string
        );

        scope.dispose();
    }

    #[test]
    fn test_keyed_services_integration() {
        let provider = ContainerBuilder::new()
            .add_named_singleton_simple::<ConsoleLogger, ConsoleLogger>(
                "console",
                ConsoleLogger::new,
            )
            .add_named_singleton_simple::<ConsoleLogger, ConsoleLogger>("file", ConsoleLogger::new)
            .add_named_instance("database_url", "postgresql://localhost/db".to_string())
            .add_named_instance("cache_url", "redis://localhost/cache".to_string())
            .build();

        // 测试命名服务解析
        let console_logger = provider
            .get_required_keyed_service::<ConsoleLogger>("console")
            .unwrap();
        let file_logger = provider
            .get_required_keyed_service::<ConsoleLogger>("file")
            .unwrap();

        console_logger.log("Console message");
        file_logger.log("File message");

        assert_eq!(console_logger.get_logs(), vec!["Console message"]);
        assert_eq!(file_logger.get_logs(), vec!["File message"]);

        // 测试命名配置
        let db_url = provider
            .get_required_keyed_service::<String>("database_url")
            .unwrap();
        let cache_url = provider
            .get_required_keyed_service::<String>("cache_url")
            .unwrap();

        assert_eq!(*db_url, "postgresql://localhost/db");
        assert_eq!(*cache_url, "redis://localhost/cache");
    }

    #[test]
    fn test_singleton_across_scopes() {
        let provider = ContainerBuilder::new()
            .add_singleton_simple::<ConsoleLogger, ConsoleLogger>(ConsoleLogger::new)
            .build();

        let mut scope1 = provider.create_scope().unwrap();
        let mut scope2 = provider.create_scope().unwrap();

        let logger1 = scope1.get_required_service::<ConsoleLogger>().unwrap();
        let logger2 = scope2.get_required_service::<ConsoleLogger>().unwrap();

        // 单例服务在不同作用域间应该有相同的日志记录
        logger1.log("test message");
        assert_eq!(logger1.get_logs().len(), logger2.get_logs().len());

        scope1.dispose();
        scope2.dispose();
    }

    #[test]
    fn test_error_handling() {
        let provider = ContainerBuilder::new().build();

        // 测试获取未注册的服务
        let result = provider.get_service::<String>();
        assert!(result.is_ok());
        assert!(result.unwrap().is_none());

        // 测试获取未注册的必需服务
        let result = provider.get_required_service::<String>();
        assert!(result.is_err());
        assert!(matches!(
            result.unwrap_err(),
            DiError::ServiceNotRegistered { .. }
        ));

        // 测试获取未注册的命名服务
        let result = provider.get_keyed_service::<String>("nonexistent");
        assert!(result.is_ok());
        assert!(result.unwrap().is_none());

        let result = provider.get_required_keyed_service::<String>("nonexistent");
        assert!(result.is_err());
        assert!(matches!(
            result.unwrap_err(),
            DiError::KeyedServiceNotRegistered { .. }
        ));
    }

    #[test]
    fn test_dyn_compatibility() {
        let provider = ContainerBuilder::new()
            .add_singleton_simple::<ConsoleLogger, ConsoleLogger>(ConsoleLogger::new)
            .build();

        // 测试 dyn trait object 的创建和使用
        let dyn_provider: &dyn ServiceProviderTrait = &provider;

        // 测试原始服务访问
        let raw_result = dyn_provider.get_service_raw(&ServiceKey::of_type::<ConsoleLogger>());
        assert!(raw_result.is_ok());
        assert!(raw_result.unwrap().is_some());

        // 测试扩展方法(需要显式导入 trait)
        let typed_result = provider.get_service::<ConsoleLogger>();
        assert!(typed_result.is_ok());
        assert!(typed_result.unwrap().is_some());
    }

    #[test]
    fn test_trait_object_storage() {
        let provider = ContainerBuilder::new()
            .add_singleton_simple::<ConsoleLogger, ConsoleLogger>(ConsoleLogger::new)
            .build();

        // 测试将 trait object 存储在集合中
        let providers: Vec<&dyn ServiceProviderTrait> = vec![&provider, &provider];

        for p in providers {
            let result = p.get_service_raw(&ServiceKey::of_type::<ConsoleLogger>());
            assert!(result.is_ok());
        }
    }
}