Skip to main content

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//! for the same reason Laravel does: 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}