4 releases

0.1.3 Aug 21, 2020
0.1.2 Aug 16, 2020
0.1.1 Aug 16, 2020
0.1.0 Aug 14, 2020

#587 in Configuration

42 downloads per month

Custom license

28KB
352 lines

GitHub Workflow Status Crates.io GitHub release (latest by date) API

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

See test suite for all supported usages.

Examples

using #[inject], call!, get! and container!.

  1. Configure a container, add some provider, e.g an Arc

    use std::sync::Arc;
    
    use ::inject::*;
    
    struct Instance(pub isize);
    
    impl Instance {
        #[inject]
        fn new(a: isize) -> Self {
            Instance(a)
        }
    }
    
    fn main() {
        let provider = Arc::new(Instance(3));
        
        // Install the Arc as a reference provider, anytime using get!
        // will resolve to a reference of this Arc.
        let container = container![
            ref provider
        ];
    
        let instance: &Instance = get!(&container, &Instance).unwrap();
        
        assert_eq!(3, instance.0)
    }
    
  2. Let the container resolve a dependency, using a closure as provider

    use ::inject::*;
    
    struct Instance(pub isize);
    
    impl Instance {
        #[inject]
        fn new(a: isize) -> Self {
            Instance(a)
        }
    }
    
    struct Service {
        a: Instance
    }
    
    impl Service {
        #[inject]
        fn new(instance: Instance) -> Self {
            Self { a: instance }
        }
    }
    
    
    fn main() {
        // Install a provider, this time a closure returning a value
        let container = container![
            |container: &Container| Ok(Instance(2))
        ];
    
        let service: Service = get!(&container, Service).unwrap();
        
        assert_eq!(service.a.0, 2)
    }
    
  3. Sometimes, calling a function with injection is useful,

    use ::inject::*;
    
    struct Service(isize);
    
    impl Service {
       #[inject]
       fn new() -> Self {
           Self(0)
       }
    }
    
    #[inject]
    fn acts_on_service(service: Service) -> isize {
       2 + service.0
    }
    
    fn main() {
       let container = container![
           |container: &Container| Ok(Service(3))
       ];
    
       let result = call!(&container, acts_on_service).unwrap();
    
       assert_eq!(result, 5)
    }
    
  4. call! supports a kwarg-flavored syntax

    use ::inject::*;
    
    struct Service(isize);
    
    impl Service {
       #[inject]
       fn new() -> Self {
           Self(0)
       }
    }
    
    #[inject]
    fn acts_on_service(service: Service) -> isize {
       2 + service.0
    }
    
    fn main() {
       let container = container![];
    
       let result = call!(&container, acts_on_service, kwargs = { service: Service(2) }).unwrap();
    
       assert_eq!(result, 4)
    }
    
  5. Dependency resolution can rely upon a type implementing the Default trait

    use ::inject::*;
    
    #[derive(Default)]
    struct Service(isize);
    
    fn main() {
       let container = container![];
       
       let service = get!(&container, Service).unwrap();
       
       assert_eq!(service.0, 0)
    }
    

Details

The get! macro with a container resolves a type in order of: installed provider (1), calling the associated inject function (often generated with #[inject]) function on a type (2), and lastly the Default trait (3).

(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).

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.

Dependencies

~1.5MB
~37K SLoC