# async-injector
[](https://github.com/udoprog/async-injector)
[](https://crates.io/crates/async-injector)
[](https://docs.rs/async-injector)
[](https://github.com/udoprog/async-injector/actions?query=branch%3Amain)
Asynchronous dependency injection for Rust.
This library provides the glue which allows for building robust decoupled
applications that can be reconfigured dynamically while they are running.
For a real world example of how this is used, see [`OxidizeBot`] for which
it was written.
## Usage
Add `async-injector` to your `Cargo.toml`.
```toml
[dependencies]
async-injector = "0.19.3"
```
## Example
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
reconfigure itself.
```rust
use async_injector::{Key, Injector, Provider};
#[derive(Debug, Clone)]
struct Database;
#[derive(Debug, Provider)]
struct Service {
#[dependency]
database: Database,
}
async fn service(injector: Injector) -> Result<(), Box> {
let mut provider = Service::provider(&injector).await?;
let Service { database } = provider.wait().await;
println!("Service got initial database {database:?}!");
let Service { database } = provider.wait().await;
println!("Service got new database {database:?}!");
Ok(())
}
```
> **Note:** This is available as the `database` example:
> ```sh
> cargo run --example database
> ```
The [`Injector`] above provides a structured broadcasting system that allows
for configuration updates to be cleanly integrated into asynchronous
contexts. The update itself is triggered by some other component that is
responsible for constructing the `Database` instance.
Building up the components of your application like this means that it can
be reconfigured without restarting it. Providing a much richer user
experience.
## Injecting multiple things of the same type
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 with different names to
discriminate them. 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`.
### A simple greeter
The following example showcases the use of `Key` to injector two different
values into an asynchronous `greeter`.
```rust
use async_injector::{Key, Injector};
async fn greeter(injector: Injector) -> Result<(), Box> {
let name = Key::::tagged("name")?;
let fun = Key::::tagged("fun")?;
let (mut name_stream, mut name) = injector.stream_key(name).await;
let (mut fun_stream, mut fun) = injector.stream_key(fun).await;
loop {
tokio::select! {
update = name_stream.recv() => {
name = update;
}
update = fun_stream.recv() => {
fun = update;
}
}
let (Some(name), Some(fun)) = (&name, &fun) else {
continue;
};
println!("Hi {name}! I see you do \"{fun}\" for fun!");
return Ok(());
}
}
```
> **Note:** you can run this using:
> ```sh
> cargo run --example greeter
> ```
The loop above can be implemented more easily using the [`Provider`] derive,
so let's do that.
```rust
use async_injector::{Injector, Provider};
#[derive(Provider)]
struct Dependencies {
#[dependency(tag = "name")]
name: String,
#[dependency(tag = "fun")]
fun: String,
}
async fn greeter(injector: Injector) -> Result<(), Box> {
let mut provider = Dependencies::provider(&injector).await?;
let Dependencies { name, fun } = provider.wait().await;
println!("Hi {name}! I see you do \"{fun}\" for fun!");
Ok(())
}
```
> **Note:** you can run this using:
> ```sh
> cargo run --example greeter_provider
> ```
## The `Provider` derive
The [`Provider`] derive can be used to conveniently implement the mechanism
necessary to wait for a specific set of dependencies to become available.
It builds a companion structure next to the type being provided called
`Provider` which in turn implements the following set of methods:
```rust
use async_injector::{Error, Injector};
impl Dependencies {
/// Construct a new provider.
async fn provider(injector: &Injector) -> Result
}
struct DependenciesProvider {
/* private fields */
}
impl DependenciesProvider {
/// Try to construct the current value. Returns [None] unless all
/// required dependencies are available.
fn build(&mut self) -> Option
/// Wait until we can successfully build the complete provided
/// value.
async fn wait(&mut self) -> Dependencies
/// Wait until the provided value has changed. Either some
/// dependencies are no longer available at which it returns `None`,
/// or all dependencies are available after which we return the
/// build value.
async fn wait_for_update(&mut self) -> Option
}
```
### Fixed arguments to `Provider`
Any arguments which do not have the `#[dependency]` attribute are known as
"fixed" arguments. These must be passed in when calling the `provider`
constructor. They can also be used during tag construction.
```rust
use async_injector::{Injector, Key, Provider};
#[derive(Provider)]
struct Dependencies {
name_tag: &'static str,
#[dependency(tag = name_tag)]
name: String,
}
async fn greeter(injector: Injector) -> Result<(), Box> {
let mut provider = Dependencies::provider(&injector, "name").await?;
let Dependencies { name, .. } = provider.wait().await;
println!("Hi {name}!");
Ok(())
}
```
[`OxidizeBot`]: https://github.com/udoprog/OxidizeBot
[cannot be hashed]: https://internals.rust-lang.org/t/f32-f64-should-implement-hash/5436
[`Injector`]: https://docs.rs/async-injector/0/async_injector/struct.Injector.html
[`Key`]: https://docs.rs/async-injector/0/async_injector/struct.Key.html
[`Provider`]: https://docs.rs/async-injector/0/async_injector/derive.Provider.html
[`serde`]: https://serde.rs
[`Stream`]: https://docs.rs/futures-core/0/futures_core/stream/trait.Stream.html
[`String`]: https://doc.rust-lang.org/std/string/struct.String.html