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.
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; }
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.
struct MyModule {}
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.
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.
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!(); ```
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.