Derive Macro async_injector_derive::Provider[][src]

#[derive(Provider)]
{
    // Attributes available to this derive:
    #[dependency]
}
Expand description

Helper derive to implement a provider.

The Provider derive can only be used on struct. Each field designates a value that must either be injected, or provided during construction.

use async_injector::Provider;
use serde::Serialize;

#[derive(Serialize)]
enum Tag {
    Table,
    Url,
}

#[derive(Provider)]
struct Deps {
    fixed: String,
    #[dependency(optional, tag = "Tag::Table")]
    table: Option<String>,
    #[dependency(tag = "Tag::Url")]
    url: String,
    #[dependency]
    connection_limit: u32,
}

This generates another struct call <ident>Provider, with the following functions:

use async_injector::{Error, Injector};

impl Deps {
    /// Construct a new provider.
    async fn provider(injector: &Injector, fixed: String) -> Result<DepsProvider, Error>
}

struct DepsProvider {
    /* private fields */
}

impl DepsProvider {
    /// Try to construct the current value. Returns [None] unless all
    /// required dependencies are available.
    fn build(&mut self) -> Option<Deps>

    /// Wait for a dependency to be updated.
    ///
    /// Once a dependency has been updated, the next call to [setup]
    /// will eagerly try to build the dependency instead of waiting for
    /// another update.
    async fn wait(&mut self) -> Deps

    /// Update and try to build the provided value.
    ///
    /// This is like combining [wait] and [build] in a manner that
    /// allows the value to be built without waiting for it the first
    /// time.
    ///
    /// The first call to [update] will return immediately, and subsequent
    /// calls will block for updates.
    async fn wait_for_update(&mut self) -> Option<Deps>
}

Field attributes

The #[dependency] attribute can be used to mark fields which need to be injected. It takes an optional #[dependency(tag = "..")], which allows you to specify the tag to use when constructing the injected Key.

use async_injector::Provider;

#[derive(Provider)]
struct DatabaseParams {
    #[dependency(tag = "\"url\"")]
    url: String,
    #[dependency]
    connection_limit: u32,
}

Examples

use async_injector::{Injector, Key, Provider};
use serde::Serialize;
use tokio_stream::StreamExt as _;

/// Fake database connection.
#[derive(Clone, Debug, PartialEq, Eq)]
struct Database {
    url: String,
    connection_limit: u32,
}

/// Provider that describes how to construct a database.
#[derive(Serialize)]
pub enum Tag {
    DatabaseUrl,
    ConnectionLimit,
}

#[derive(Provider)]
struct DatabaseParams {
    #[dependency(tag = "Tag::DatabaseUrl")]
    url: String,
    #[dependency(tag = "Tag::ConnectionLimit")]
    connection_limit: u32,
}

let db_url_key = Key::<String>::tagged(Tag::DatabaseUrl)?;
let conn_limit_key = Key::<u32>::tagged(Tag::ConnectionLimit)?;

let injector = Injector::new();

let database_injector = injector.clone();
let mut database = DatabaseParams::provider(&injector).await?;

tokio::spawn(async move {
    loop {
        match database.update().await {
            Some(update) => {
                database_injector.update(Database {
                    url: update.url,
                    connection_limit: update.connection_limit,
                }).await;
            }
            None => {
                database_injector.clear::<Database>().await;
            }
        }
    }
});

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.recv().await;

// Database instance is available!
assert_eq!(
    new_database,
    Some(Database {
        url: String::from("example.com"),
        connection_limit: 5
    })
);

Ok(())