Asynchronous dependency injection for Rust.
This crate provides a dependency injection system that can be used to reactively reconfigure you're application while it's running. Reactive in this case refers to the application being reconfigured as-the-value changes, and not for other typical scenarios such as when it's being restarted.
Values are provided as [Stream]s of updates that can be subscribed to as necessary throughout your application.
In the following we'll showcase the injection of a fake Database. The
idea here would be that if something about the database connection changes,
a new instance of Database would be created and cause the application to
update.
This is available as the
fake_databaseexample and you can run it yourself using:sh cargo run --example fake_database
```rust use tokio::time; use tokio_stream::StreamExt as _;
struct Database;
async fn main() {
    let injector = asyncinjector::setup();
    let (mut databasestream, mut database) = injector.stream::
// Insert the database dependency in a different task in the background.
tokio::spawn({
    let injector = injector.clone();
    async move {
        time::sleep(time::Duration::from_secs(2)).await;
        injector.update(Database).await;
    }
});
assert!(database.is_none());
// Every update to the stored type will be streamed, allowing you to
// react to it.
if let Some(update) = database_stream.next().await {
    println!("Updating database!");
    database = update;
} else {
    panic!("No database update received :(");
}
assert!(database.is_some());
} ```
The [Injector] provides a structured broadcast system of updates, that can integrate cleanly into asynchronous contexts.
With a bit of glue, this means that your application can be reconfigured without restarting it. Providing a richer user experience.
In the previous section you might've noticed that the injected value was
solely discriminated by its type: Database. In this example we'll show how
[Key] can be used to tag values of the same type under different names.
This can be useful when dealing with overly generic types like [String].
The tag used must be serializable with [serde]. It must also not use any
components which [cannot be hashed], like f32 and f64. This will
otherwise cause an error to be raised as it's being injected.
```rust use asyncinjector::Key; use serde::Serialize; use std::{error::Error, time::Duration}; use tokio::time; use tokiostream::StreamExt as _;
enum Tag { One, Two, }
async fn main() -> Result<(), Box
tokio::spawn({
    let injector = injector.clone();
    let one = one.clone();
    async move {
        let mut interval = time::interval(Duration::from_secs(1));
        for i in 0u32.. {
            interval.tick().await;
            injector.update_key(&one, i).await;
        }
    }
});
tokio::spawn({
    let injector = injector.clone();
    let two = two.clone();
    async move {
        let mut interval = time::interval(Duration::from_secs(1));
        for i in 0u32.. {
            interval.tick().await;
            injector.update_key(&two, i * 2).await;
        }
    }
});
let (mut one_stream, mut one) = injector.stream_key(one).await;
let (mut two_stream, mut two) = injector.stream_key(two).await;
println!("one: {:?}", one);
println!("two: {:?}", two);
loop {
    tokio::select! {
        Some(update) = one_stream.next() => {
            one = update;
            println!("one: {:?}", one);
        }
        Some(update) = two_stream.next() => {
            two = update;
            println!("two: {:?}", two);
        }
    }
}
} ```
Provider deriveThe following showcases how the [Provider] derive can be used to automatically construct and inject dependencies.
```rust use asyncinjector::{Key, Provider}; use serde::Serialize; use tokiostream::StreamExt as _; use std::error::Error;
/// Fake database connection.
struct Database { url: String, connection_limit: u32, }
impl Database {
    async fn build(provider: DatabaseProvider) -> Option
/// Provider that describes how to construct a database.
pub enum Tag { DatabaseUrl, ConnectionLimit, }
struct DatabaseProvider { #[dependency(tag = "Tag::DatabaseUrl")] url: String, #[dependency(tag = "Tag::ConnectionLimit")] connection_limit: u32, }
async fn testprovider() -> Result<(), Box
let injector = async_injector::setup();
tokio::spawn(DatabaseProvider::run(injector.clone()));
let (mut database_stream, database) = injector.stream::<Database>().await;
// None of the dependencies are available, so it hasn't been constructed.
assert!(database.is_none());
assert!(injector
    .update_key(&db_url_key, String::from("example.com"))
    .await
    .is_none());
assert!(injector.update_key(&conn_limit_key, 5).await.is_none());
let new_database = database_stream
    .next()
    .await
    .expect("unexpected end of stream");
// Database instance is available!
assert_eq!(
    new_database,
    Some(Database {
        url: String::from("example.com"),
        connection_limit: 5
    })
);
Ok(())
} ```