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(())