use auto_di::{
Lazy, Provider, application, configuration, global_container, provider, resolve, singleton,
};
use std::{
sync::{
Arc,
atomic::{AtomicUsize, Ordering},
},
time::Duration,
};
#[derive(Debug)]
struct AppConfig {
application_name: String,
database_url: String,
}
#[derive(Debug)]
struct Database {
url: String,
}
#[provider]
fn build_number() -> u64 {
42
}
#[derive(Default)]
struct ApplicationBeans;
#[configuration]
impl ApplicationBeans {
#[provider]
fn app_config(&self) -> AppConfig {
AppConfig {
application_name: "Khatarnak DI".into(),
database_url: "postgres://localhost/example".into(),
}
}
#[provider]
async fn database(&self, config: Arc<AppConfig>) -> Database {
println!("connecting database asynchronously...");
tokio::time::sleep(Duration::from_millis(20)).await;
Database {
url: config.database_url.clone(),
}
}
}
trait PaymentGateway: Send + Sync {
fn name(&self) -> &'static str;
}
struct StripeGateway;
impl PaymentGateway for StripeGateway {
fn name(&self) -> &'static str {
"stripe"
}
}
struct RazorpayGateway;
impl PaymentGateway for RazorpayGateway {
fn name(&self) -> &'static str {
"razorpay"
}
}
#[singleton(name = "stripe", primary)]
fn stripe_gateway() -> Arc<dyn PaymentGateway> {
Arc::new(StripeGateway)
}
#[singleton(name = "razorpay")]
fn razorpay_gateway() -> Arc<dyn PaymentGateway> {
Arc::new(RazorpayGateway)
}
struct CheckoutService {
default_gateway: Arc<dyn PaymentGateway>,
indian_gateway: Arc<dyn PaymentGateway>,
database: Arc<Database>,
}
#[derive(Debug)]
struct CheckoutChannel(&'static str);
#[singleton(eager, post_construct = "start", pre_destroy = "stop")]
impl CheckoutService {
fn new(
default_gateway: Arc<dyn PaymentGateway>,
#[qualifier("razorpay")] indian_gateway: Arc<dyn PaymentGateway>,
database: Arc<Database>,
) -> Self {
Self {
default_gateway,
indian_gateway,
database,
}
}
async fn start(&self) {
println!("CheckoutService post-construct hook");
}
async fn stop(&self) {
println!("CheckoutService pre-destroy hook");
}
#[provider]
fn checkout_channel(&self) -> CheckoutChannel {
CheckoutChannel("web")
}
}
static JOB_IDS: AtomicUsize = AtomicUsize::new(1);
#[derive(Debug)]
struct BackgroundJob {
id: usize,
}
#[singleton(scope = "prototype")]
fn background_job() -> BackgroundJob {
BackgroundJob {
id: JOB_IDS.fetch_add(1, Ordering::SeqCst),
}
}
#[derive(Debug)]
struct RequestMetadata;
#[singleton(scope = "request")]
fn request_metadata() -> RequestMetadata {
RequestMetadata
}
struct MissingCache;
struct ReportService {
cache: Option<Arc<MissingCache>>,
}
#[singleton]
impl ReportService {
fn new(cache: Option<Arc<MissingCache>>) -> Self {
Self { cache }
}
}
struct Dashboard {
reports: Provider<ReportService>,
lazy_reports: Lazy<ReportService>,
}
#[singleton]
impl Dashboard {
fn new(reports: Provider<ReportService>, lazy_reports: Lazy<ReportService>) -> Self {
Self {
reports,
lazy_reports,
}
}
}
#[application]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let config = resolve::<AppConfig>().await?;
println!("application: {}", config.application_name);
println!("standalone provider: build {}", resolve::<u64>().await?);
println!(
"provider from singleton impl: {}",
resolve::<CheckoutChannel>().await?.0
);
let checkout = resolve::<CheckoutService>().await?;
println!("primary gateway: {}", checkout.default_gateway.name());
println!("qualified gateway: {}", checkout.indian_gateway.name());
println!("database: {}", checkout.database.url);
let first_job = resolve::<BackgroundJob>().await?;
let second_job = resolve::<BackgroundJob>().await?;
println!("prototype job ids: {}, {}", first_job.id, second_job.id);
let request = global_container()?.request_context();
let first_metadata = request.resolve::<RequestMetadata>().await?;
let second_metadata = request.resolve::<RequestMetadata>().await?;
println!(
"same request-scoped instance: {}",
Arc::ptr_eq(&first_metadata, &second_metadata)
);
let dashboard = resolve::<Dashboard>().await?;
let report = dashboard.reports.get().await?;
let lazy_report = dashboard.lazy_reports.get().await?;
println!("optional cache available: {}", report.cache.is_some());
println!(
"lazy singleton reused: {}",
Arc::ptr_eq(&report, lazy_report)
);
Ok(())
}