Skip to main content

Provider

Trait Provider 

Source
pub trait Provider<S>:
    Any
    + Send
    + Sync
    + 'static {
    // Provided methods
    fn name(&self) -> &'static str { ... }
    fn boot_priority(&self) -> Option<u8> { ... }
    fn run_priority(&self) -> Option<u8> { ... }
    fn order(&self) -> ProviderOrder { ... }
    fn boot<'life0, 'life1, 'async_trait>(
        &'life0 self,
        _state: &'life1 S,
    ) -> Pin<Box<dyn Future<Output = Result<()>> + Send + 'async_trait>>
       where Self: 'async_trait,
             'life0: 'async_trait,
             'life1: 'async_trait { ... }
    fn shutdown<'life0, 'life1, 'async_trait>(
        &'life0 self,
        _state: &'life1 S,
    ) -> Pin<Box<dyn Future<Output = Result<()>> + Send + 'async_trait>>
       where Self: 'async_trait,
             'life0: 'async_trait,
             'life1: 'async_trait { ... }
    fn validate(&self, _state: &S) -> Result<()> { ... }
    fn as_any(&self) -> &dyn Any
       where Self: Sized { ... }
    fn as_reloadable(&self) -> Option<&dyn Reloadable<S>> { ... }
    fn as_runnable(self: Arc<Self>) -> Option<Arc<dyn Runnable<S>>> { ... }
}
Expand description

Any service that can be registered in the DI registry.

§Lifecycle convention

Each provider lives in four explicit phases. Mixing work across phase boundaries is the most common bug source — keep them strict.

  1. register() (free fn, outside the trait) — synchronous, no async, called once during bootstrap. Constructs the provider in a placeholder/empty state and inserts it into the registry.

    Allowed:

    • Read state-level inputs (state.run_mode(), state.config_dir()) to choose what to register.
    • Read on-disk config synchronously only if the answer decides whether to register the provider at all (e.g. feature toggles, worker pinning). Use std::fs here — register is sync.

    Forbidden:

    • Resolving other providers from the registry (they may not exist yet; ordering is settled by Provider::order() and coarse priority, not by register order).
    • Async I/O.
    • Spawning tasks.
    • Building the operational snapshot (that’s boot()).
  2. boot() — async, called after every register() ran, in lifecycle order. This is where the provider becomes usable.

    Allowed / expected:

    • Resolve dependencies from the registry — by now every other register() has run.
    • Async I/O — tokio::fs for config, network calls, etc. Never std::fs (it blocks the runtime).
    • Build the runtime snapshot and publish it via ArcSwap / ArcSwapOption so concurrent readers see atomic swaps.
    • Honor disabled-state from config: leave the snapshot empty and return Ok(()) rather than failing.

    Forbidden:

    • Spawning long-running tasks. Boot must return when state is ready; the long task lives in Runnable::run().
  3. Runnable::run() — see that trait. The only place for long-lived loops; honors disabled-state by returning Ok(()) immediately.

  4. shutdown() — async best-effort cleanup after the shutdown signal has fired and before the runtime aborts any remaining runnable tasks. Use this for process-owned resources that must not leak into the next graceful boot. Default no-op.

Reload (Reloadable::reload()) follows the same shape as boot().

Provided Methods§

Source

fn name(&self) -> &'static str

Human-readable label for logs/diagnostics.

Source

fn boot_priority(&self) -> Option<u8>

Optional boot priority. Lower values run earlier among otherwise-ready providers. None means priority::NORMAL. Prefer Provider::order() for dependency relationships; priority is only a coarse tie-breaker.

Source

fn run_priority(&self) -> Option<u8>

Optional runtime task start priority. Lower values run earlier. None means priority::NORMAL.

Source

fn order(&self) -> ProviderOrder

Optional type-based boot/reload ordering hints.

The registry builds one ordered lifecycle plan and uses it for boot, validate, shutdown, and reload. Reload skips providers that are not Reloadable, but dependency relationships remain the same: reload is a boot emulation on a live process.

Source

fn boot<'life0, 'life1, 'async_trait>( &'life0 self, _state: &'life1 S, ) -> Pin<Box<dyn Future<Output = Result<()>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait,

Bootstrap-time async initialization. See the trait-level lifecycle convention for what belongs here vs in register() / run(). Default no-op so providers that only need register() insertion don’t have to implement this.

Source

fn shutdown<'life0, 'life1, 'async_trait>( &'life0 self, _state: &'life1 S, ) -> Pin<Box<dyn Future<Output = Result<()>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait,

Graceful-shutdown cleanup hook.

This is not a replacement for Drop: it is the lifecycle point for externally named resources whose stale presence can break the next boot, such as shm segments or lock files. Implementations should be idempotent because shutdown paths may be re-entered.

Source

fn validate(&self, _state: &S) -> Result<()>

Synchronous preflight validation. Runs in the config-check / startup validation phase before any boot() to fail fast on bad config (missing files, conflicting settings) without touching the registry.

Source

fn as_any(&self) -> &dyn Any
where Self: Sized,

Downcast hook for typed resolve APIs.

Source

fn as_reloadable(&self) -> Option<&dyn Reloadable<S>>

Optional capability hook.

Source

fn as_runnable(self: Arc<Self>) -> Option<Arc<dyn Runnable<S>>>

Optional capability hook.

Dyn Compatibility§

This trait is dyn compatible.

In older versions of Rust, dyn compatibility was called "object safety".

Implementors§