Skip to content

Commit

Permalink
Better documentation
Browse files Browse the repository at this point in the history
  • Loading branch information
R1tschY committed Sep 1, 2020
1 parent ffcf227 commit 094f1d0
Show file tree
Hide file tree
Showing 6 changed files with 385 additions and 68 deletions.
12 changes: 9 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,14 @@

*Dependency injection frameworks for Rust*

Two dependency injection frameworks live in this project:
* `chassis` - A compile time dependency injection framework inspired by Dagger 2
* `dyn-chassis` - A runtime dependency injection framework inspired by Guice
Two dependency injection frameworks live in this repository

## [`chassis`]

compile-time dependency injection framework inspired by Dagger 2

[`chassis`]: ./chassis/README.md

## `dyn-chassis`

runtime dependency injection framework inspired by Guice
6 changes: 3 additions & 3 deletions chassis/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
[package]
name = "chassis"
version = "0.1.0"
authors = ["R1tschY <[email protected]>"]
authors = ["Richard Liebscher <[email protected]>"]
edition = "2018"
license = "MIT OR Apache-2.0"
description = "Compile time dependency injection framework"
description = "Compile-time dependency injection framework"
repository = "https://github.com/R1tschY/chassis"
readme = "README.md"
keywords = ["dependency-injection"]
keywords = ["dependency-injection", "inversion-of-control", "di"]

[lib]
proc-macro = true
Expand Down
119 changes: 118 additions & 1 deletion chassis/README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,122 @@
# Chassis

*Compile-time dependency injector for Rust inspiried by Dagger 2*
*Compile-time dependency injector for Rust inspired by Dagger 2*

## Goals
* Detect errors at compile time like missing dependencies or cyclic dependencies
* No need to annotate your classes (support for third-party classes)
* No required usage of `std::sync::Arc`
* Zero overhead: Fast as hand-written code
* No use of runtime type information

## Features

* Unscoped: create a new instance everytime
* default
* Type must not implement `Clone`
* Singletons: only one instance per component
* Type must implement `Clone`
* Created when component is created

## Example
```rust
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
#[chassis::integration]
mod integration {
use super::*;

pub struct DemoModule;

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

/// Define how to create your dependencies
impl DemoModule {
#[singleton]
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`.
pub trait DemoComponent {
/// request the to create injection code for
/// our main class `Greeter`
fn resolve_greeter(&self) -> Greeter;
}
}

fn main() {
// import component trait
use crate::integration::DemoComponent;

// use generated component implementation
let injector = integration::DemoComponentImpl::new();

// Resolve main dependency
// Note: it can not fail at runtime!
let greeter = injector.resolve_greeter();

// enjoy!
greeter.say_hello();
}
```

### Missing features
* Request reference (access singletons without cloning)
* Lazy singletons (create singletons when needed)
* Lazy requests (request a factory instead of concrete type)
* Optional requests (only get it when it exists)
* Multiple provider (useful for plugins)
* Failable module functions (return `Result` in module)
69 changes: 8 additions & 61 deletions chassis/examples/static_greeter.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
use std::error::Error;

use chassis::integration;

pub struct Greeter {
Expand All @@ -23,85 +21,34 @@ impl Greeter {
mod int_mod {
use super::*;

pub struct DemoFactory;
pub struct DemoModule;

pub type Message = String;
pub type Count = i32;
pub struct Message(String);
pub struct Count(i32);

#[module]
impl DemoFactory {
/* [singleton(lazy = true)] */
impl DemoModule {
pub fn provide_count() -> Count {
5
Count(5)
}

/* [singleton(lazy = false)] */
pub fn provide_message() -> Message {
"Hello World".to_string()
Message("Hello World".to_string())
}

pub fn provide_greeter(message: Message, count: Count) -> Greeter {
Greeter::new(message.to_string(), count)
Greeter::new(message.0, count.0)
}
}

#[component(modules = [DemoFactory], send = false, sync = false)]
pub trait DemoComponent {
fn resolve_greeter(&self) -> Greeter;
}
}

// TODO: Idea for introspection of modules: compile time visitor
// trait Visitable {
// fn accept<T: Visitor>();
// }
//
// impl Visitable for int_mod::DemoModule {
// fn accept<T: Visitor>() {
// Visitor::visit::<Greeter>();
// }
// }
//
// trait Visitor {
// fn visit<T>();
// }

// GENERATED

/*struct ChassisDemoComponent {
// module1: DemoModule,
}
impl ChassisDemoComponent {
pub fn new() -> Self {
Self {
//module1: DemoModule::default()
}
}
pub fn build(module1: DemoModule) -> Self {
Self {
//module1
}
}
}
impl DemoComponent for ChassisDemoComponent {
fn resolve_greeter() -> Arc<Greeter> {
Arc::new(DemoModule::provide_greeter(
Arc::new(DemoModule::provide_message()),
Arc::new(DemoModule::provide_count()),
))
}
}*/

// /GENERATED

fn main() -> Result<(), Box<dyn Error>> {
fn main() {
use crate::int_mod::DemoComponent;

let injector = int_mod::DemoComponentImpl::new();
let greeter = injector.resolve_greeter();
greeter.say_hello();
Ok(())
}
12 changes: 12 additions & 0 deletions chassis/src/key.rs
Original file line number Diff line number Diff line change
Expand Up @@ -304,6 +304,18 @@ impl ToKeyStr for syn::TypeImplTrait {
mod tests {
use super::*;

#[test]
fn check_type() {
let ty: syn::Type = syn::parse2(quote! { String }).unwrap();
assert_eq!("String", ty.to_key_str().unwrap());
}

#[test]
fn check_paren() {
let ty: syn::Type = syn::parse2(quote! { ((String)) }).unwrap();
assert_eq!("String", ty.to_key_str().unwrap());
}

#[test]
fn check_path() {
let ty: syn::Type = syn::parse2(quote! { a::b }).unwrap();
Expand Down
Loading

0 comments on commit 094f1d0

Please sign in to comment.