13 stable releases

Uses old Rust 2015

new 1.5.0 Oct 21, 2020
1.4.0 Oct 18, 2020
1.2.2 Sep 7, 2020
1.2.1 Aug 29, 2020
Download history 36/week @ 2020-08-18 79/week @ 2020-08-25 46/week @ 2020-09-01 30/week @ 2020-09-08 7/week @ 2020-09-15 18/week @ 2020-09-22 35/week @ 2020-09-29 17/week @ 2020-10-06 32/week @ 2020-10-13

112 downloads per month
Used in waiter_di

MIT license

32KB
796 lines

Dependency injection for Rust

How to use:

Cargo.toml:

waiter_di = "1.5.0"

lib.rs and any other file, that uses library:

use waiter_di::*;

See examples/1_get_started.rs for minimal example of usage.

See examples/2_modules.rs for example with modules and constructors.

See examples/3_inject_options_list.rs for demo of all available injection options.

How to use

Annotate structure with #[component]

#[component]
struct Comp {}

Annotate impl blocks with #[provides]

#[provides]
impl Interface for Comp {}

Create container:

fn main() {
    let mut container = Container::<profiles::Default>::new();
}

Get dependency ref:

fn main() {
    let comp = Provider::<dyn Interface>::get(&mut container);
}

Inject references

For Rc:

#[component]
struct Dependency;

#[component]
struct Comp {
    dependency_rc: Rc<Dependency>
}

fn main() {
    let mut container = Container::<profiles::Default>::new();
    Provider::<Comp>::get(&mut container);
}

To create new struct instead of getting reference:

#[component]
struct Comp {
    dependency: Dependency,
    dependency_box: Box<Dependency>
}

fn main() {
    let mut container = Container::<profiles::Default>::new();
    Provider::<Comp>::create(&mut container);
    Provider::<Comp>::create_boxed(&mut container);
}

Properties

It uses config crate under the hood, for example it tries to find float_prop in args as --float_prop <value>, if not found it tries to find it in environment variables, after that tries config/{profile}.toml, after that config/default.toml

#[derive(Debug, Deserialize)]
struct ConfigObject {
    i32_prop: i32
}

#[component]
struct Comp {
    config: Config,
    #[prop("int")] int_prop: usize,
    #[prop("int")] int_prop_opt: Option<usize>,
    #[prop("int" = 42)] int_prop_with_default_value: usize,
    float_prop: f32,
    #[prop] config_object: ConfigObject
}

Dependency cycle

Use Deferred type:

#[component]
struct Comp {
    dependency_def: Deferred<Dependency>,
    dependency_def_rc: Deferred<Rc<Dependency>>,
    dependency_def_box: Deferred<Box<Dependency>>
}

Profiles

You can use predefined profiles from `waiter_di::profile" or create custom:

struct CustomProfile;

#[provides(profiles::Dev, CustomProfile)]
impl Interface for Comp {}

fn main() {
    let mut container = Container::<profiles::Default>::new();
    let mut container = Container::<profiles::Dev>::new();
    let mut container = Container::<CustomProfile>::new();
}

Get profile from args, environment or config/default.toml

Just define property named profile as --profile <profile> arg, profile env variable or profile property in config/default.toml and use inject! macro:

fn main() {
    let comp = inject!(Comp: profiles::Default, profiles::Dev);
}

inject! macro can't be used for several components, so it's recommended to use it with modules:

#[module]
struct SomeModule {
    component: Component
}
#[module]
struct RootModule {
    some_module: SomeModule
}
fn main() {
    let root_module = inject!(RootModule: profiles::Default, profiles::Dev);
}

In this case #[module] is just a synonym for #[component]

Factory functions:

If you can't use #[component] annotation, use factory function instead:

#[provides]
fn create_dependency(bool_prop: bool) -> Dependency {
    Dependency { prop: bool_prop }
}

To use it like a constructor, use it with #[component] on impl block:

struct Comp();

#[component]
impl Comp {
    #[provides]
    fn new() -> Self {
        Self()
    }
}

Deferred args in factory functions is unsupported. In the rest it can accept the same arg types as #[component].

External types isn't supported for factory functions:

#[provides] // won't compile
fn create_external_type_dependency() -> HashMap<i32, i32> {
    HashMap::new()
}

So you need to create crate-local wrapper:

struct Wrapper(HashMap<i32, i32>);

#[provides]
fn create_external_type_dependency() -> Wrapper {
    Wrapper(HashMap::new())
}

For convenience you can use #[wrapper] attribute to implement Deref automatically:

#[wrapper]
struct HashMap(std::collections::HashMap<i32, i32>);

#[provides]
fn create_external_type_dependency() -> HashMap {
    return HashMap(std::collections::HashMap::<i32, i32>::new());
}

Dependencies

~1.4–2.2MB
~60K SLoC