Compile-time dependency injector for Rust inspired by Dagger 2
- No need to annotate your classes (support for third-party classes)
- No required usage of
std::sync::Arc
- Zero overhead: Fast as handwritten code
- No use of runtime type information (provided by
std::any::Any
references) - At the moment relying on compiler optimization untilmin_specialization
is stabilized.
- No use of runtime type information (provided by
- (Detect errors at compile time like missing dependencies or cyclic dependencies)
- Waiting for compile time reflection or at least stabilization of
min_specialization
,const_type_id
andconst_cmp_type_id
- Waiting for compile time reflection or at least stabilization of
Add chassis
to your crate dependencies
[dependencies]
chassis = "^0.2.0"
Structs will be modules that can provide dependencies with functions and that itself can have dependencies. Note: Currently only associated functions are supported!
#[derive(Default)]
pub struct Module;
#[chassis::module]
impl Module {
pub fn provide_something(dep1: Dependency1, dep2: Dependency2) -> Dependency3 {
Dependency3::new(dep1, dep2)
}
// ...
}
Traits will be components. For each trait an implemented component will be created.
The generated implementation will have a Impl
suffix, for example ComponentImpl
. Also a
Component::new
function is created.
#[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();
}
Normally for every needed dependency the provider function on the module is called. This results
in types created multiple times. This is maybe not intended. The solution is to use a
singleton
attribute. The provide method will than only called once at build time of the
component (call to ComponentImpl::new
). The requirement is that the type implements the
Clone
trait. It is recommendable to use a shared reference type like Rc
or Arc
for
singletons so that really only one instance is created.
#[chassis::module]
impl Module {
#[chassis(singleton)]
pub fn provide_printer() -> Rc<dyn Printer> {
Rc::new(StdoutPrinter)
}
}
- Lifetimes in the types are not supported (except
'static
) - Generics are not handled correctly
- Request a reference to a registered non-reference type in a module
(
&MyType
whenMyType
is provided by a module) - Lazy requests (request a provider instead of concrete type)
- Optional requests (only get it when it exists)
- Multiple provider (useful for plugins)
- Failable module functions (return
Result
in module)
Licensed under either of
- Apache License, Version 2.0 (LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0)
- MIT license (LICENSE-MIT or http://opensource.org/licenses/MIT)
at your option.
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.