Skip to main content

DynProvider

Struct DynProvider 

Source
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::Client
  • sqlx::SqlitePool
  • redis::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>
where T: Send + Sync + 'static,

Source

pub fn new<F, Fut>(f: F) -> DynProvider<T>
where F: Fn() -> Fut + Send + Sync + 'static, Fut: Future<Output = Result<T, InjectableError>> + Send + 'static,

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()) })
Source

pub fn sync<F>(f: F) -> DynProvider<T>
where F: Fn() -> Result<T, InjectableError> + Send + Sync + 'static,

Create a DynProvider from a sync closure returning InjectableResult<T>.

Use this for synchronous construction of external types. This is the most ergonomic option for simple cases.

§Example
DynProvider::sync(|| Ok(HttpClient::new(5000)))
Source

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?)
})
Source

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()));
Source

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)));

Auto Trait Implementations§

§

impl<T> !RefUnwindSafe for DynProvider<T>

§

impl<T> !UnwindSafe for DynProvider<T>

§

impl<T> Freeze for DynProvider<T>

§

impl<T> Send for DynProvider<T>

§

impl<T> Sync for DynProvider<T>

§

impl<T> Unpin for DynProvider<T>

§

impl<T> UnsafeUnpin for DynProvider<T>

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.