pub struct DynProvider<T> { /* private fields */ }Expand description
A dynamic, closure-based provider for types that cannot derive Injectable.
This is the key building block for injecting external types — types
from third-party crates that you don’t control and therefore can’t add
#[derive(Injectable)] to.
§When to Use
Use DynProvider when you need to inject a type you don’t own:
reqwest::Clientsqlx::SqlitePoolredis::Client- Any type from a dependency
§How It Works
Instead of a compile-time generated provider, DynProvider wraps an
async closure that constructs the value. The closure receives an
Arc<ResolveContext> so it can itself resolve dependencies.
§Registration
DynProvider instances are registered via ContainerBuilder::register()
in the public injectable crate:
let container = Container::builder()
.register("", DynProvider::new(async {
Ok(reqwest::Client::new())
}))
.build()
.await?;Or with context access for dependent construction:
let container = Container::builder()
.register("", DynProvider::with_ctx(|ctx| async move {
let config = ctx.resolve::<Config>().await?;
Ok(Database::connect(&config.connection_string).await?)
}))
.build()
.await?;Implementations§
Source§impl<T> DynProvider<T>
impl<T> DynProvider<T>
Sourcepub fn new<F, Fut>(f: F) -> DynProvider<T>
pub fn new<F, Fut>(f: F) -> DynProvider<T>
Create a DynProvider from a closure that returns a future.
Use this for types that can be constructed without resolving other dependencies from the container. The closure is called each time the provider is invoked, producing a fresh future.
§Example
DynProvider::new(|| async { Ok(reqwest::Client::new()) })Sourcepub fn sync<F>(f: F) -> DynProvider<T>
pub fn sync<F>(f: F) -> DynProvider<T>
Sourcepub fn with_ctx<F, Fut>(f: F) -> DynProvider<T>where
F: Fn(FactoryCtx) -> Fut + Send + Sync + 'static,
Fut: Future<Output = Result<T, InjectableError>> + Send + 'static,
pub fn with_ctx<F, Fut>(f: F) -> DynProvider<T>where
F: Fn(FactoryCtx) -> Fut + Send + Sync + 'static,
Fut: Future<Output = Result<T, InjectableError>> + Send + 'static,
Create a DynProvider from a closure that receives a FactoryCtx.
Use this for types that need to resolve other dependencies during
construction. FactoryCtx exposes only scope-safe operations
(extract and resolve_external) so the factory cannot bypass the
singleton cache or violate transient/singleton scope semantics.
§Migrating from ctx.resolve::<T>()
// Before (bypassed singleton cache):
DynProvider::with_ctx(|ctx| async move {
let config = ctx.resolve::<AppConfig>().await?; // ← dangerous
Ok(Database::connect(&config.db_url).await?)
})
// After (scope-safe):
DynProvider::with_ctx(|ctx| async move {
let config: Inject<AppConfig> = ctx.extract().await?;
Ok(Database::connect(&config.db_url).await?)
})Sourcepub fn from_value(value: T) -> DynProvider<T>where
T: Clone,
pub fn from_value(value: T) -> DynProvider<T>where
T: Clone,
Register a pre-built value. On each resolution the value is cloned.
Useful in tests to inject a pre-configured mock without writing a closure:
container.register("", DynProvider::from_value(MockDb::default()));Sourcepub fn from_arc(arc: Arc<T>) -> DynProvider<T>where
T: Clone,
pub fn from_arc(arc: Arc<T>) -> DynProvider<T>where
T: Clone,
Register a pre-built Arc<T>. On each resolution the inner value is cloned.
Use this when you already hold an Arc<T> and want to avoid double-wrapping:
let shared = Arc::new(MockDb::default());
container.register("", DynProvider::from_arc(Arc::clone(&shared)));