3 个不稳定版本

0.2.0 2023 年 9 月 15 日
0.1.1 2021 年 4 月 21 日
0.1.0 2020 年 9 月 1 日

Rust 模式 中排名 672

每月下载 38

MIT/Apache 许可证

27KB
333

底盘

受 Dagger 2 启发的 Rust 编译时依赖注入器

目标

  • 无需对类进行注解(支持第三方类)
  • 无需使用 std::sync::Arc
  • 零开销:与手写代码一样快
    • 不使用运行时类型信息(由 std::any::Any 引用提供) - 目前依赖于编译器优化,直到 min_specialization 稳定。
  • (在编译时检测错误,如缺少依赖项或循环依赖项)

使用

chassis 添加到您的 crate 依赖项中

[dependencies]
chassis = "^0.2.0"

结构体将是模块,可以提供具有函数的依赖项,并且自身也可以有依赖项。注意:目前仅支持关联函数!

#[derive(Default)]
pub struct Module;

#[chassis::module]
impl Module {
    pub fn provide_something(dep1: Dependency1, dep2: Dependency2) -> Dependency3 {
        Dependency3::new(dep1, dep2)
    }
    // ...
}

特质将是组件。对于每个特质,将创建一个实现的组件。生成的实现将具有 Impl 后缀,例如 ComponentImpl。还创建了一个 Component::new 函数。

#[chassis::injector(modules = [Module])]
pub trait Component {
    fn resolve_main_class(&self) -> MainClass;
}

示例

use std::rc::Rc;
// define your business logic
/// printer trait
pub trait Printer {
    fn print(&self, input: &str);
}

/// a printer implementation
pub struct StdoutPrinter;
impl Printer for StdoutPrinter {
    fn print(&self, input: &str) {
        println!("{}", input);
    }
}

/// greeter for messages
pub struct Greeter {
    message: String,
    printer: Rc<dyn Printer>,
}

impl Greeter {
    /// constructor with dependencies
    pub fn new(message: String, printer: Rc<dyn Printer>) -> Self {
        Self { message, printer }
    }
    /// your business logic
    pub fn say_hello(&self) {
        self.printer.print(&self.message);
    }
}

/// module that is parsed to create the dependency injection code
#[derive(Default)]
pub struct DemoModule;

// use strong types when in need to distinguish
pub struct Message(String);

/// Define how to create your dependencies
#[chassis::module]
impl DemoModule {
    pub fn provide_printer() -> Rc<dyn Printer> {
        Rc::new(StdoutPrinter)
    }
    pub fn provide_message() -> Message {
        Message("Hello World".to_string())
    }
    pub fn provide_greeter(
        message: Message,
        printer: Rc<dyn Printer>
    ) -> Greeter {
        Greeter::new(message.0, printer)
    }
}

/// Define which dependencies you need.
///
/// A struct `DemoComponentImpl` will be created for
/// you which implements `DemoComponent`.
#[chassis::injector(modules = [DemoModule])]
pub trait DemoComponent {
    /// request the to create injection code for our main class `Greeter`
    fn resolve_greeter(&self) -> Greeter;
}

fn main() {
    // use generated component implementation
    let injector = <dyn DemoComponent>::new()
        .expect("DI container should be consistent");
    // Resolve main dependency
    // Note: it can not fail at runtime!
    let greeter = injector.resolve_greeter();
    // enjoy!
    greeter.say_hello();
}

单例

通常,对于每个所需的依赖项,都会在模块中调用提供函数。这会导致类型被多次创建。这可能是无意为之。解决方案是使用 singleton 属性。然后将在组件构建时(对 ComponentImpl::new 的调用)仅调用一次提供方法。要求类型实现 Clone 特质。建议对于单例使用共享引用类型,如 RcArc,以便真正只创建一个实例。

示例

#[chassis::module]
impl Module {
    #[chassis(singleton)]
    pub fn provide_printer() -> Rc<dyn Printer> {
        Rc::new(StdoutPrinter)
    }
}

限制

  • 类型中的生命周期不支持(除 'static 外)
  • 泛型处理不正确
  • 在模块中请求对已注册的非引用类型引用(当 MyType 由模块提供时,请请求 &MyType
  • 懒请求(请求提供者而不是具体类型)
  • 可选请求(仅当存在时获取)
  • 多个提供者(对插件很有用)
  • 可能失败的模块函数(在模块中返回 Result

许可证

以下任一许可证下授权

任选其一。

贡献

除非您明确声明,否则您提交的任何有意包含在作品中的贡献(根据 Apache-2.0 许可证定义),将按照上述方式双授权,不附加任何额外条款或条件。

依赖

~0.6–1MB
~25K SLoC