flow-di 0.1.0

A dependency injection framework for Rust inspired by C# AutoFac and Microsoft.Extensions.DependencyInjection
Documentation
# Flow-DI


[![Rust](https://img.shields.io/badge/rust-1.70+-orange.svg)](https://www.rust-lang.org)
[![Crates.io](https://img.shields.io/crates/v/flow-di.svg)](https://crates.io/crates/flow-di)
[![Documentation](https://docs.rs/flow-di/badge.svg)](https://docs.rs/flow-di)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)

A Rust dependency injection framework inspired by C# AutoFac and Microsoft.Extensions.DependencyInjection.

[中文文档](README_CN.md) | English

## 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
- **Object Safety**: Core traits are dyn-compatible for flexible usage patterns

## Quick Start


Add this to your `Cargo.toml`:

```toml
[dependencies]
flow-di = "0.1.0"
```

### Basic Usage


```rust
use flow_di::{ContainerBuilder, ServiceProviderExt};
use std::sync::Arc;

// Define service interface
trait IRepository: Send + Sync {
    fn get_data(&self) -> String;
}

// Implement service
struct DatabaseRepository {
    connection_string: String,
}

impl IRepository for DatabaseRepository {
    fn get_data(&self) -> String {
        format!("Data from database: {}", self.connection_string)
    }
}

fn main() {
    // Configure and build container
    let provider = ContainerBuilder::new()
        .add_instance("localhost:5432".to_string()) // Register configuration
        .add_singleton_with_deps::<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());
}
```

### Lifetime Management


#### Singleton

Services registered as singleton will have only one instance throughout the application lifetime:

```rust
use flow_di::{ContainerBuilder, ServiceProviderExt};

let provider = ContainerBuilder::new()
    .add_singleton_simple::<String, String>(|| "singleton instance".to_string())
    .build();

let instance1 = provider.get_required_service::<String>().unwrap();
let instance2 = provider.get_required_service::<String>().unwrap();
// instance1 and instance2 are the same instance
assert!(Arc::ptr_eq(&instance1, &instance2));
```

#### Scoped

Services registered as scoped will share the same instance within the same scope:

```rust
use flow_di::{ContainerBuilder, ServiceProviderExt};

let provider = ContainerBuilder::new()
    .add_scoped_simple::<String, String>(|| "scoped instance".to_string())
    .build();

let mut scope = provider.create_scope().unwrap();
let service1 = scope.get_required_service::<String>().unwrap();
let service2 = scope.get_required_service::<String>().unwrap();
// service1 and service2 are the same instance within the same scope
assert!(Arc::ptr_eq(&service1, &service2));
```

#### Transient

Services registered as transient will create a new instance for each request:

```rust
use flow_di::{ContainerBuilder, ServiceProviderExt};

let provider = ContainerBuilder::new()
    .add_transient_simple::<String, String>(|| "transient instance".to_string())
    .build();

let instance1 = provider.get_required_service::<String>().unwrap();
let instance2 = provider.get_required_service::<String>().unwrap();
// Each time is a new instance
assert!(!Arc::ptr_eq(&instance1, &instance2));
```

### Named Services


You can register multiple implementations of the same interface using different names:

```rust
use flow_di::{ContainerBuilder, ServiceProviderExt};

// Register named services
let provider = ContainerBuilder::new()
    .add_named_singleton_simple::<String, String>("primary", || "primary service".to_string())
    .add_named_singleton_simple::<String, String>("secondary", || "secondary service".to_string())
    .build();

// Resolve named services
let primary = provider.get_required_keyed_service::<String>("primary").unwrap();
let secondary = provider.get_required_keyed_service::<String>("secondary").unwrap();

assert_eq!(*primary, "primary service");
assert_eq!(*secondary, "secondary service");
```

### Dependency Injection


Services can depend on other services, and dependencies will be automatically injected:

```rust
use flow_di::{ContainerBuilder, ServiceProviderExt};
use std::sync::Arc;

trait ILogger: Send + Sync {
    fn log(&self, message: &str);
}

trait IRepository: Send + Sync {
    fn get_user(&self, id: i32) -> String;
}

struct ConsoleLogger;
impl ILogger for ConsoleLogger {
    fn log(&self, message: &str) {
        println!("Log: {}", message);
    }
}

struct UserRepository {
    logger: Arc<ConsoleLogger>,
}

impl IRepository for UserRepository {
    fn get_user(&self, id: i32) -> String {
        self.logger.log(&format!("Getting user {}", id));
        format!("User {}", id)
    }
}

struct UserService {
    repository: Arc<UserRepository>,
    logger: Arc<ConsoleLogger>,
}

impl UserService {
    fn new(repository: Arc<UserRepository>, logger: Arc<ConsoleLogger>) -> Self {
        Self { repository, logger }
    }

    fn get_user(&self, id: i32) -> String {
        self.logger.log("UserService::get_user called");
        self.repository.get_user(id)
    }
}

let provider = ContainerBuilder::new()
    // Register logger
    .add_singleton_simple::<ConsoleLogger, ConsoleLogger>(|| ConsoleLogger)
    
    // Register repository with logger dependency
    .add_scoped_with_deps::<UserRepository, UserRepository, ConsoleLogger>(
        |logger| UserRepository { logger }
    )
    
    // Register service with multiple dependencies
    .add_transient_with_deps2::<UserService, UserService, UserRepository, ConsoleLogger>(
        |repository, logger| UserService::new(repository, logger)
    )
    .build();

let mut scope = provider.create_scope().unwrap();
let user_service = scope.get_required_service::<UserService>().unwrap();
let user = user_service.get_user(123);
println!("{}", user); // Output: User 123
```

### Service Scopes


Service scopes provide isolation for scoped services and automatic resource cleanup:

```rust
use flow_di::{ContainerBuilder, ServiceProviderExt};

let provider = ContainerBuilder::new()
    .add_scoped_simple::<String, String>(|| "scoped service".to_string())
    .build();

// Create scope
let mut scope = provider.create_scope().unwrap();

// ServiceScope also implements ServiceProvider, so it can be used to resolve services
let service = scope.get_required_service::<String>().unwrap();
println!("{}", service);

// Release scope
scope.dispose();

// Check if disposed
let is_disposed = scope.is_disposed();
assert!(is_disposed);
```

## API Reference


### ContainerBuilder


The `ContainerBuilder` provides a fluent API for registering services:

#### Basic Registration Methods


- `add_singleton<TService, TImplementation>(factory)` - Register singleton service
- `add_scoped<TService, TImplementation>(factory)` - Register scoped service
- `add_transient<TService, TImplementation>(factory)` - Register transient service
- `add_instance<T>(instance)` - Register singleton instance

#### Simple Registration Methods (No Dependencies)


- `add_singleton_simple<TService, TImplementation>(factory)` - Register singleton service without dependencies
- `add_scoped_simple<TService, TImplementation>(factory)` - Register scoped service without dependencies
- `add_transient_simple<TService, TImplementation>(factory)` - Register transient service without dependencies

#### Dependency Injection Methods


- `add_singleton_with_deps<TService, TImplementation, TDep>(factory)` - Register singleton with one dependency
- `add_singleton_with_deps2<TService, TImplementation, TDep1, TDep2>(factory)` - Register singleton with two dependencies
- Similar methods for `scoped` and `transient`

#### Named Service Methods


- `add_named_singleton<TService, TImplementation>(name, factory)` - Register named singleton
- `add_named_scoped<TService, TImplementation>(name, factory)` - Register named scoped service
- `add_named_transient<TService, TImplementation>(name, factory)` - Register named transient service
- `add_named_instance<T>(name, instance)` - Register named singleton instance

### ServiceProvider


The `ServiceProvider` provides methods for resolving services:

#### Core Methods (Object-Safe)


- `get_service_raw(&self, key: &ServiceKey)` - Get raw service as `Arc<dyn Any>`

#### Extension Methods (via ServiceProviderExt trait)


- `get_service<T>()` - Get optional service of type T
- `get_required_service<T>()` - Get required service of type T (panics if not found)
- `get_keyed_service<T>(key)` - Get optional named service
- `get_required_keyed_service<T>(key)` - Get required named service
- `create_scope()` - Create a new service scope

### ServiceScope


Service scopes provide isolated contexts for scoped services:

- Implements `ServiceProviderExt` trait
- Automatic resource cleanup on disposal
- Thread-safe operations
- Nested scope support

## Architecture


### Object Safety


Flow-DI is designed with object safety in mind:

- `ServiceProvider` trait is object-safe and can be used as `dyn ServiceProvider`
- `ServiceProviderExt` provides generic methods via extension trait pattern
- Automatic implementation for all `ServiceProvider` implementors

### Thread Safety


All components are thread-safe:

- Services can be resolved from multiple threads
- Scopes can be created and managed concurrently
- Singleton instances are safely shared across threads

### Memory Management


- Uses `Arc<T>` for service instances to enable safe sharing
- Automatic cleanup of scoped services when scopes are disposed
- No memory leaks from circular dependencies (detection prevents them)

## Examples


See the [examples](examples/) directory for complete examples:

- [Basic Usage]examples/basic_usage.rs - Comprehensive example showing all features

## Contributing


Contributions are welcome! Please feel free to submit a Pull Request.

## License


This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.

## Acknowledgments


- Inspired by [AutoFac]https://autofac.org/ for .NET
- Inspired by [Microsoft.Extensions.DependencyInjection]https://docs.microsoft.com/en-us/dotnet/core/extensions/dependency-injection