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 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) -> Option<&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 boot_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 boot_priority 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. None means priority::NORMAL. Use priority::FIRST for providers others depend on (e.g. HttpService publishing the parsed http.yaml), priority::AFTER / priority::LATE for consumers.

Source

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

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

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.

Call this before spawning runnable providers so bad config, missing files, or conflicting settings fail fast before long-running work starts. Applications may choose whether validation happens before or after boot_all, depending on whether a provider needs boot-time state to validate itself.

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) -> Option<&dyn Runnable<S>>

Optional capability hook.

Implementors§