rainier_container/provider.rs
1//! [`ServiceProvider`] — the unit of framework wiring.
2//!
3//! Every Rainier component ships a provider rather than a `setup()` function,
4//! because registration and booting have to happen in
5//! two separate passes across *all* providers, or a provider could resolve a
6//! service that a later provider has not bound yet.
7//!
8//! - [`register`](ServiceProvider::register) binds factories into the
9//! container. It must **not** resolve anything, because the providers after
10//! it have not registered yet.
11//! - [`boot`](ServiceProvider::boot) runs once every provider has registered,
12//! so by then it may resolve freely. It is `async`, which is what lets a
13//! provider open a database pool or warm a cache during boot.
14
15use std::any::type_name;
16
17use rainier_support::{BoxFuture, Result};
18
19use crate::application::Application;
20
21/// A bundle of related bindings and their boot-time wiring.
22///
23/// ```
24/// # use rainier_container::{Application, Container, ServiceProvider};
25/// # use rainier_support::Result;
26/// struct Clock;
27/// struct ClockProvider;
28///
29/// impl ServiceProvider for ClockProvider {
30/// fn register(&self, app: &Application) -> Result<()> {
31/// app.singleton(|_: &Container| Ok(Clock));
32/// Ok(())
33/// }
34/// }
35/// ```
36pub trait ServiceProvider: Send + Sync + 'static {
37 /// A label for diagnostics and `provider:list`. Defaults to the Rust type
38 /// name.
39 fn name(&self) -> &'static str {
40 type_name::<Self>()
41 }
42
43 /// Bind services into the container.
44 ///
45 /// Runs for every provider before any provider boots. Do not resolve here.
46 fn register(&self, app: &Application) -> Result<()> {
47 let _ = app;
48 Ok(())
49 }
50
51 /// Wire things together once every provider has registered.
52 ///
53 /// Safe to resolve from. Returns a boxed future so the trait stays
54 /// object-safe — providers are held as `Arc<dyn ServiceProvider>`.
55 fn boot<'a>(&'a self, app: &'a Application) -> BoxFuture<'a, Result<()>> {
56 let _ = app;
57 Box::pin(async { Ok(()) })
58 }
59}
60
61/// Implements [`ServiceProvider::boot`] from an `async fn`, so a provider can
62/// be written with ordinary async syntax instead of hand-boxing the future.
63///
64/// ```
65/// # use rainier_container::{boot_provider, Application, Container, ServiceProvider};
66/// # use rainier_support::Result;
67/// struct Pool;
68/// struct DatabaseProvider;
69///
70/// impl ServiceProvider for DatabaseProvider {
71/// fn register(&self, app: &Application) -> Result<()> {
72/// app.singleton(|_: &Container| Ok(Pool));
73/// Ok(())
74/// }
75///
76/// boot_provider!(async |self, app| {
77/// let _pool = app.resolve::<Pool>()?;
78/// Ok(())
79/// });
80/// }
81/// ```
82#[macro_export]
83macro_rules! boot_provider {
84 (async |$self:ident, $app:ident| $body:block) => {
85 fn boot<'boot>(
86 &'boot $self,
87 $app: &'boot $crate::Application,
88 ) -> $crate::__private::BoxFuture<'boot, $crate::__private::Result<()>> {
89 ::std::boxed::Box::pin(async move { $body })
90 }
91 };
92}