auto-di 0.2.0

Ergonomic async-aware automatic dependency injection for Rust
Documentation
# auto-di

Automatic dependency injection for Rust, backed by a global `inventory`
registry and async-safe initialization. The core API intentionally stays
small: use `#[singleton]` for constructor injection and `#[provider]` for factory
injection.

## Components and constructor injection

```rust
use std::sync::Arc;
use auto_di::singleton;

trait Users: Send + Sync {}

#[singleton(name = "postgres", primary)]
fn users() -> Arc<dyn Users> {
    Arc::new(PostgresUsers)
}

#[singleton(eager, post_construct = "start", pre_destroy = "stop")]
impl UserService {
    fn new(#[qualifier("postgres")] users: Arc<dyn Users>) -> Self {
        Self { users }
    }

    async fn start(&self) {}
    async fn stop(&self) {}
}
```

`#[singleton]` supports these options:

- `name = "..."`
- `primary`
- `scope = "singleton" | "prototype" | "request"`
- `eager`
- `profile = "development"`
- `condition = "ENV_KEY"` or `condition = "ENV_KEY=value"`
- `post_construct = "async_method"`
- `pre_destroy = "async_method"`

Constructors accept `Arc<T>`, `Option<Arc<T>>`, `Vec<Arc<T>>`, `Provider<T>`,
and `Lazy<T>`. Both sync and async constructors are supported.

## Providers

Providers can be declared in any module:

```rust
#[provider]
fn database_pool() -> DatabasePool {
    DatabasePool::new()
}
```

Singletons automatically discover nested providers:

```rust
#[singleton]
impl UserService {
    fn new(repository: Arc<UserRepository>) -> Self {
        Self { repository }
    }

    #[provider]
    fn user_cache(&self) -> UserCache {
        UserCache::new()
    }
}
```

```rust
#[derive(Default)]
struct Beans;

#[configuration]
impl Beans {
    #[provider]
    fn config(&self) -> Config { Config::default() }

    #[provider(name = "main", primary)]
    async fn database(&self, config: Arc<Config>) -> Database {
        Database::connect(&config).await
    }
}
```

## Environment properties

```rust
#[configuration_properties("database")]
struct DatabaseProperties {
    url: String,       // DATABASE_URL
    pool_size: usize,  // DATABASE_POOL_SIZE
}
```

## Application and request scopes

```rust
#[application]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let service = auto_di::resolve::<UserService>().await?;
    Ok(())
}

let request = auto_di::global_container()?.request_context();
let dependency = request.resolve::<RequestDependency>().await?;
```

Active profiles come from comma-separated `APP_PROFILES`, or can be selected
explicitly with `Container::with_profiles(...)`.