1个不稳定版本

0.2.0 2023年9月15日

#2014进程宏


用于 chassis

MIT/Apache

45KB
1K SLoC

Chassis

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

目标

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

使用

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–1.1MB
~25K SLoC