#di #ioc #dependency-injection #dependencies #derive-debug #debugging

rudi

Rudi - an out-of-the-box dependency injection framework for Rust

16 releases (7 breaking)

0.8.3 Mar 26, 2024
0.8.1 Feb 26, 2024
0.7.0 Dec 6, 2023
0.6.0 Sep 11, 2023

#267 in Rust patterns

Download history 45/week @ 2024-06-10 13/week @ 2024-06-24 39/week @ 2024-07-01 7/week @ 2024-07-08 38/week @ 2024-07-15 8/week @ 2024-07-22 55/week @ 2024-07-29 9/week @ 2024-08-05 6/week @ 2024-08-19 47/week @ 2024-08-26 3/week @ 2024-09-02 21/week @ 2024-09-09 15/week @ 2024-09-16 94/week @ 2024-09-23

144 downloads per month
Used in 2 crates

MIT/Apache

145KB
2K SLoC

Rudi

Crates.io version docs.rs docs

English | 简体中文

Rudi - an out-of-the-box dependency injection framework for Rust.

use rudi::{Context, Singleton, Transient};

// Register `fn(cx) -> A { A }` as the constructor for `A`
#[derive(Debug)]
#[Transient]
struct A;

#[derive(Debug)]
struct B(A);

// Register `fn(cx) -> B { B::new(cx.resolve::<A>()) }` as the constructor for `B`
#[Transient]
impl B {
    #[di]
    fn new(a: A) -> B {
        B(a)
    }
}

// Register `fn(cx) -> C { C::B(cx.resolve::<B>()) }` as the constructor for `C`
#[allow(dead_code)]
#[Transient]
enum C {
    A(A),

    #[di]
    B(B),
}

// Register `fn(cx) -> () { Run(cx.resolve::<B>(), cx.resolve::<C>()) }` as the constructor for `()`
#[Singleton]
fn Run(b: B, c: C) {
    println!("{:?}", b);
    assert!(matches!(c, C::B(_)));
}

fn main() {
    // Automatically register all types and functions with the `#[Singleton]`, `#[Transient]` or `#[SingleOwner]` attribute.
    let mut cx = Context::auto_register();

    // Get an instance of `()` from the `Context`, which will call the `Run` function.
    // This is equivalent to `cx.resolve::<()>();`
    cx.resolve()
}

Features

  • Three scopes: Singleton, Transient and SingleOwner (example).
  • Async functions and async constructors.
  • Attribute macros can be used on struct, enum, impl block and function.
  • Manual and automatic registration (thanks to inventory).
  • Easy binding of trait implementations and trait objects.
  • Distinguishing different instances with types and names.
  • Generics (but must be monomorphized and manually registered) (example).
  • Conditional registration (example).
  • References (only Singleton and SingleOwner scope) (example).

More complex example

use std::{fmt::Debug, rc::Rc};

use rudi::{Context, Singleton, Transient};

// Register `async fn(cx) -> i32 { 42 }` as the constructor for `i32`,
// and specify the name of the instance of this `i32` type as `"number"`.
#[Singleton(name = "number")]
async fn Number() -> i32 {
    42
}

// Register `async fn(cx) -> Foo { Foo { number: cx.resolve_with_name_async("number").await } }`
// as the constructor for `Foo`, and specify the name of the instance of this `Foo` type as `"foo"`.
#[derive(Debug, Clone)]
#[Singleton(async, name = "foo")]
struct Foo {
    #[di(name = "number")]
    number: i32,
}

#[derive(Debug)]
struct Bar(Foo);

impl Bar {
    fn into_debug(self) -> Rc<dyn Debug> {
        Rc::new(self)
    }
}

// Register `async fn(cx) -> Bar { Bar::new(cx.resolve_with_name_async("foo").await).await }`
// as the constructor for `Bar`.
//
// Bind the implementation of the `Debug` trait and the trait object of the `Debug` trait,
// it will register `asycn fn(cx) -> Rc<dyn Debug> { Bar::into_debug(cx.resolve_async().await) }`
// as the constructor for `Rc<dyn Debug>`.
#[Transient(binds = [Self::into_debug])]
impl Bar {
    #[di]
    async fn new(#[di(name = "foo")] f: Foo) -> Bar {
        Bar(f)
    }
}

#[Singleton]
async fn Run(bar: Bar, debug: Rc<dyn Debug>, #[di(name = "foo")] f: Foo) {
    println!("{:?}", bar);
    assert_eq!(format!("{:?}", bar), format!("{:?}", debug));
    assert_eq!(format!("{:?}", bar.0.number), format!("{:?}", f.number));
}

#[tokio::main]
async fn main() {
    let mut cx = Context::auto_register();

    cx.resolve_async().await
}

More examples can be found in the examples and tests directories.

Credits

  • Koin: This project's API design and test cases were inspired by Koin.
  • inventory: This project uses inventory to implement automatic registration, making Rust's automatic registration very simple.

Contributing

Thanks for your help improving the project! We are so happy to have you!

License

Licensed under either of

Contribution

Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.

Dependencies

~0.4–1MB
~21K SLoC