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.
-
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::fshere — 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()).
- Read state-level inputs (
-
boot()— async, called after everyregister()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::fsfor config, network calls, etc. Neverstd::fs(it blocks the runtime). - Build the runtime snapshot and publish it via
ArcSwap/ArcSwapOptionso 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().
- Resolve dependencies from the registry — by now every other
-
Runnable::run()— see that trait. The only place for long-lived loops; honors disabled-state by returningOk(())immediately. -
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§
Sourcefn boot_priority(&self) -> Option<u8>
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.
Sourcefn run_priority(&self) -> Option<u8>
fn run_priority(&self) -> Option<u8>
Optional runtime task start priority. Lower values run earlier.
None means priority::NORMAL.
Sourcefn order(&self) -> ProviderOrder
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.
Sourcefn 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 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.
Sourcefn 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 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.
Sourcefn validate(&self, _state: &S) -> Result<()>
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.
Sourcefn as_reloadable(&self) -> Option<&dyn Reloadable<S>>
fn as_reloadable(&self) -> Option<&dyn Reloadable<S>>
Optional capability hook.
Dyn Compatibility§
This trait is dyn compatible.
In older versions of Rust, dyn compatibility was called "object safety".