[−][src]Derive Macro async_injector_derive::Provider
#[derive(Provider)] { // Attributes available to this derive: #[provider] #[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.
Container attributes
The #[provider]
attribute is an attribute that must be used.
The available attributes are:
#[provider(output = "...")]
- which is used to specify the type of the provided value.#[provider(build = "...")]
(optional) - which can be used to specify a custom build function. If no build function is specified, the value will always be cleared.#[provider(clear = "...")]
(optional) - which can be used to specify a custom clear function.
We must specify the output type of the Provider
using #[provider(output = "...")]
like this:
use async_injector::Provider; #[derive(Provider)] #[provider(output = "Database")] struct DatabaseProvider { #[dependency(tag = "\"url\"")] url: String, #[dependency] connection_limit: u32, } #[derive(Debug, Clone)] struct Database;
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)] #[provider(output = "Database", build = "Database::build", clear = "Database::clear")] struct DatabaseProvider { #[dependency(tag = "\"url\"")] url: String, #[dependency] connection_limit: u32, } #[derive(Debug, Clone)] struct Database { url: String, } impl Database { /// Logic for how to build the injected value with all dependencies available. async fn build(provider: DatabaseProvider) -> Option<Self> { Some(Self { url: provider.url, }) } /// Logic for how to clear the injected value. async fn clear() -> Option<Self> { None } }
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, } impl Database { async fn build(provider: DatabaseProvider2) -> Option<Self> { Some(Self { url: provider.url, connection_limit: provider.connection_limit, }) } } /// Provider that describes how to construct a database. #[derive(Serialize)] pub enum Tag { DatabaseUrl, ConnectionLimit, } #[derive(Provider)] #[provider(output = "Database", build = "Database::build")] struct DatabaseProvider2 { #[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(); tokio::spawn(DatabaseProvider2::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(())