Crate dill

Crate dill 

Source
Expand description

Runtime dependency injection.

Documentation is under construction!

§Examples

§Basic dependency resolution

As a user of type A we only care about getting an instance to use - the life-cycle of A and its dependencies remain hidden from us.

use dill::*;
use std::sync::Arc;

#[component]
struct A {
    b: Arc<B>,  // An instance of `B` will be resolved and injected when requesting `A`
}

impl A {
    fn foo(&self) -> String {
        format!("a::{}", self.b.bar())
    }
}

#[component]
struct B;

impl B {
    fn bar(&self) -> String {
        "b".to_string()
    }
}

let catalog = Catalog::builder()
  .add::<A>()
  .add::<B>()
  .build();

let a = catalog.get_one::<A>().unwrap();
assert_eq!(a.foo(), "a::b");

§Using trait objects (aka Interfaces)

Every type can be associated with multiple traits that it implements using CatalogBuilder::bind() method, allowing dynamically picking the best implementation to use (e.g. based on config) or even using multiple implementations at once (e.g. plugins).

use dill::*;
use std::sync::Arc;

// An interface that has two implementations below
trait A: Send + Sync {
    fn foo(&self) -> String;
}

#[component]
struct AImpl1;
impl A for AImpl1 {
    fn foo(&self) -> String {
        "aimpl1".to_string()
    }
}

#[component]
struct AImpl2;
impl A for AImpl2 {
    fn foo(&self) -> String {
        "aimpl2".to_string()
    }
}

let catalog = Catalog::builder()
  .add::<AImpl1>()
  .bind::<dyn A, AImpl1>()
  .add::<AImpl2>()
  .bind::<dyn A, AImpl2>()
  .build();

// AllOf<T> is a DependencySpec that returns instances of all types that implement trait T
let ays = catalog.get::<AllOf<dyn A>>().unwrap();

let mut foos: Vec<_> = ays.iter().map(|a| a.foo()).collect();
foos.sort(); // Order is undefined

assert_eq!(foos, vec!["aimpl1".to_owned(), "aimpl2".to_owned()]);

§Controlling lifetimes with Scopes

The life-cycle of a type is no longer controlled by the user of a type. Author of type A below can choose whether A should be created per call (Transient) or reused by all clients (Singleton).

use dill::*;

#[component]
#[scope(Singleton)]
struct A {
    // Needed for compiler not to optimize type out
    name: String,
}

impl A {
    fn test(&self) -> String {
        format!("a::{}", self.name)
    }
}

let cat = Catalog::builder()
    .add::<A>()
    .add_value("foo".to_owned())
    .build();

let inst1 = cat.get::<OneOf<A>>().unwrap();
let inst2 = cat.get::<OneOf<A>>().unwrap();

// Expecting Singleton scope to return same instance
assert_eq!(
    inst1.as_ref() as *const A,
    inst2.as_ref() as *const A
);

§Parametrizing builders

Builders can be parametrized during the registration process for convenience (e.g. with values read from configuration).

use dill::*;

#[component]
#[scope(Singleton)]
struct ConnectionPool {
    host: String,
    port: i32,
}

impl ConnectionPool {
    fn url(&self) -> String {
        format!("http://{}:{}", self.host, self.port)
    }
}

let cat = Catalog::builder()
    .add_builder(
        ConnectionPool::builder()
            .with_host("foo".to_owned())
            .with_port(8080),
    )
    .build();

let inst = cat.get::<OneOf<ConnectionPool>>().unwrap();
assert_eq!(inst.url(), "http://foo:8080");

Re-exports§

pub use scopes::*;
pub use specs::*;

Modules§

cache
scopes
specs
utils

Structs§

AmbiguousTypeError
Catalog
CatalogBuilder
CatalogWeakRef
A weak reference to the Catalog that can be safely injected into the components and used as a factory. While generally considered an anti-pattern, this provides a great flexibility over when and how instances are constructed.
DependencyInfo
InjectionContext
InjectionStack
Lazy
Represents a value whose construction is delayed upon request rather than resolved from the catalog immediately. This is often useful in cases when some expensive type is used rarely, thus it’s beneficial to only construct it on-demand.
ScopeInversionError
TypeInfo
TypecastBuilder
Takes a dynamic Builder and casts the instance to desired interface
TypedBuilderWithoutDefaultInterfaces
A wrapper builder that stops it from auto-registering default interfaces
UnregisteredTypeError
ValidationError

Enums§

InjectionError
InjectionStackFrame

Traits§

Builder
Builders are responsible for resolving dependencies and creating new instances of a certain type. Builders typically create new instances for every call, delegating the lifetime management to Scopes,
BuilderExt
Component
Allows CatalogBuilder::add() to accept types with associated builder
TypedBuilder
TypedBuilderCast
TypedBuilderExt
ValidationErrorExt

Attribute Macros§

component
interface
meta
scope