Giter Site home page Giter Site logo

zihantype / rudi Goto Github PK

View Code? Open in Web Editor NEW
72.0 2.0 3.0 260 KB

Rudi - an out-of-the-box dependency injection framework for Rust -- Rudi,一个开箱即用的 Rust 依赖注入框架

License: Apache License 2.0

Shell 0.27% Rust 99.73%
dependency dependency-injection di ioc

rudi's Introduction

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.

rudi's People

Contributors

zihantype avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar

rudi's Issues

Support for dependency's field injection.

When inject a specific field from a struct, for example, I think it would be better to code like this:

// Register async function and specify name
#[Singleton(name = "number")]
async fn Number() -> u32 {
    42
}

#[derive(Debug, Clone)]
#[Singleton(name = "config")] // Register async constructor and specify name
struct Config{
    #[di(name = "number")] // Specify the name of the dependency
    max_user: u32,
}

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

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

#[Transient(binds = [Self::into_debug])] // Bind the implementation of the `Debug` trait and the trait object of the `Debug` trait
impl Bar {
    async fn new(#[di(name = "config.max_user")] max_user: u32) -> Bar { // Register async constructor
        Bar(max_user)
    }
}

eager_create configuration does not take effect

Example code:

use rudi::Context;
use rudi_macro::Singleton;

#[Singleton(eager_create)]
fn Run() {
    println!("Run()");
}


#[test]
fn singleton_owned() {
    Context::auto_register();

    Context::options().eager_create(false).auto_register();

    Context::options().eager_create(false).allow_only_single_eager_create(false).auto_register();


}

Anticipation:
Only the first one that defaults to true executes the Run, the rest of the ones specified as false do not execute anything with eager_create

image

The code in the red box in the figure and is seen to validate the eager_create configuration.

I also have a question for you.
Why is the function call in the red box in the above picture called twice in flash, is there any special mechanism? I'm sorry I don't have a thorough understanding of the project's source code, so I'd like to ask

Parentheses should be omitted when just use #[di()] in struct field.

In current version, If I need to inject a i32 for a struct field without specified dependency name, I need to type #[di()]

Here's my usecase:

#[Singleton]
async fn Number() -> i32 {
    452
}

#[Singleton(async)]
struct Config {
    #[di()]
    number: i32,
    #[di(default = 123)]
    test: u32,
}

It would be better to write it just as #[di].
Meanwhile, if I don't need to specify test to 123, I can just remove the #[di(default = 123)], and derive Default for Config, tell the provider to use u32::default() for initializing this field

The better syntax may be like this:

#[Singleton]
async fn Number() -> i32 {
    452
}

#[derive(Default)]
#[Singleton(async)]
struct Config {
    #[di]
    number: i32,
    test: u32,
}

Error handling

  • To better handle Errors during instantiation, use try_resolve_xxx or try_get_xxx to get the instance

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.