[−][src]Derive Macro async_injector_derive::Provider
#[derive(Provider)] { // Attributes available to this derive: #[dependency] }
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) /// 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 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 .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(())