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: ```rust 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 }

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

[componentmodulemanifest]

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(); asserteq!(greeter.greet(), "helloworld 1"); // Internal states of the greeter is kept. asserteq!(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.