lockjaw 0.1.0

Compile time dependency injection framework inspired by dagger
Documentation

Lockjaw

Lockjaw is a fully static, compile-time dependency injection framework for Rust inspired by Dagger. It is also what you get when jabbed by a rusty dagger.

Features:

  • Compile time dependency graph validation with helpful diagnostic messages. The code won't compile if a dependency is missing or there are cyclic dependencies.
  • Cross-crate injection. Libraries can provide different implementations like prod and test, and allow clients to choose from them.
  • Aims for feature parity with Dagger. If you have used Dagger before, lockjaw should feel familiar.
    • Implemented:
      • @Inject => #[injectable] member injection for concrete class
      • @Provides => #[provides] bind method return values
      • @Binds => #[binds] bind trait to implementation.
      • @Singleton / @Scope => scope="component" shared instance.
      • @Named => type NamedType = Type;
    • To do:
      • @Inject constructor injection
      • Provider<T> create multiple instances at run time.
      • Lazy<T> create and cache instance only when used.
      • Subcomponents Dynamically creatable sub-scopes with additional bindings
      • Multibindings Collect same bindings to a set/map, useful for plugin systems.
      • @BindsOptionalOf Allow some bindings to be missing
      • Factories create objects with both injected fields and runtime fields.
      • Producers async dependency injection. Might not be too useful comparing to async/await

See user guide for more information.

Example:

use lockjaw::*;
use std::ops::Add;
// Allow GreetCounter to be created in the dependency graph. These bindings are available anywhere.
#[injectable]
struct GreetCounter{
    // Auto initialize with Default::default()
    counter : ::std::cell::RefCell<i32>
}

impl GreetCounter{
    pub fn increment(&self) -> i32 {
        let mut m = self.counter.borrow_mut();
        *m = m.add(1);
        m.clone()
    }
}

pub trait Greeter {
    fn greet(&self) -> String;
}

#[injectable]
struct GreeterImpl {
    // Initialize with an instance of GreetCounter
    #[inject]
    greet_counter : crate::GreetCounter,
    // Initialize with an instance of String
    #[inject]
    phrase : String
}

impl Greeter for GreeterImpl{
    fn greet(&self) -> String{
        format!("{} {}", self.phrase, self.greet_counter.increment())
    }
}

// Declare a module so we can do special bindings. These bindings are only available if the
// component installs the module, so different bindings can be used based on the situation.
#[module]
struct MyModule {}

#[module_impl]
impl MyModule {
    // When ever someone needs a Greeter, use GreeterImpl as the actual implementation 
    #[binds]
    pub fn bind_greeter(_impl : crate::GreeterImpl) -> impl crate::Greeter {}

    // Called when a String is requested
    #[provides]
    pub fn provide_string() -> String {
        "helloworld".to_owned()
    }
}

// A list of modules.
#[component_module_manifest]
struct ModuleManifest {
    my_module : crate::MyModule
}

// Components stitch modules and injectables together into a dependency graph, and can create
// objects in the graph. This coponent installs modules listed in ModuleManifest, which is MyModule.
#[component(modules = "crate::ModuleManifest")]
trait MyComponent {
    // Allows creating a greeter with the component. The created object has the lifetime of the
    // component
    fn greeter(&'_ self) -> MaybeScoped<'_, dyn crate::Greeter>;
}

pub fn main() {
    // Creates the component
    let component = MyComponent::new();
    // Creates a greeter.
    let greeter = component.greeter();
    assert_eq!(greeter.greet(), "helloworld 1");
    // Internal states of the greeter is kept.
    assert_eq!(greeter.greet(), "helloworld 2");
    
    // A new greeter has a new independent set of injected objects.
    assert_eq!(component.greeter().greet(), "helloworld 1");
}
// called after the last use of lockjaw to perform validation and code generation
epilogue!();

Disclaimer

This is not an officially supported Google product.

Lockjaw is currently in early development and all APIs are subjected to changes. Some feature are also implemented in a hacky way. Use at your own risk.