inject 0.1.0

Experimental IOC library for Rust
Documentation

Rust

Experimental IOC library inpsired by injector for Rust. Goals: IOC + ergonomics.

See test suite for all supported usages.

Example quick-fire usages:

use std::sync::Arc;

use ::inject::*;

struct Instance(pub isize);

impl Instance {
    #[inject]
    pub fn new(a: isize) -> Self {
        Instance(a)
    }
}

struct Service {
    a: Instance
}

impl Default for Service {
    fn default() -> Self {
        Service{ a: Instance(0) }
    }
}

impl Service {
    #[inject(default(instance = Instance(4)), no_inject(instance))]
    pub fn new(instance: Instance) -> Self {
        Self { a: instance }
    }
}

#[inject]
fn injectable(a: isize, b: &Instance) -> isize {
    a + b.0
}

fn main() {
    let provider = Arc::new(Instance(3));
    let container = container![
        ref provider,
        |_: &_| Ok(Instance(1))
    ];
    let instance = get!(&container, Instance).unwrap();
    
    let result = call!(&container, injectable, kwargs = { b: &instance }).unwrap();
    
    // isize::default() + Instance(1).0
    assert_eq!(result, 1);
    
    // 12 + Arc(Instance(3)).as_ref().0
    let result = call!(&container, injectable, kwargs = { a: 12 }).unwrap();
    
    assert_eq!(result, 15);
    
    let service = get!(&container, Service).unwrap();
    
    assert_eq!(service.a.0, 1); // 1 since value-provider returns Instance(1)
    
    let container = container![]; // new container, no providers
    
    let service = get!(&container, Service).unwrap();
    
    assert_eq!(service.a.0, 4); // The injection specific default, resolved since no provider for Instance
    
    let service = Service::default();
    
    assert_eq!(service.a.0, 0); // The trivial default impl, untouched
}

The get! macro with a container resolves a type in order of: installed provider (1), calling the associated inject function on a type (2), and lastly the Default implementation by a blanket impl of the InjectExt trait (3) by the default associated function resolution order.

The interaction with the Default impl is to imitiate ergonomics in injector.

(2) & (3) can be opt-out by attribute #[inject(no_inject(arg))], (name tbd) in which case only container held provider will be used for resolution of the type. Method specific defaults are annotated as #[inject(defualt(arg = expression))] where expression will lazy evaluate on failing attempt at (1) and (2).

call! macro behaves similiarily to get!, but supports kwargs.

Todo:

  1. Support kwargs for "constructors" with a create_object! flavored macro.
  2. Make #[inject] support Struct attribute notation with #[inject(..)] for individual struct fields.
  3. Make default and no_inject story less annoying.