Factory
Sometimes an object needs not only injected values but also runtime values to be created, such as constructor parameters that depends on user input.
This can be handled by writing a factory that injects bindings as a Provider, and combine it with the runtime value to create the object. For example,
pub struct Foo {
pub i: i32, // runtime
pub s: String, // injected
}
pub struct FooFactory<'component>{
s_provider: Provider<'component, String>
}
#[injectable]
impl FooFacotory<'_> {
#[inject]
pub fn new(s_provider: Provider<String>) -> FooFactory {
FooFactory { s_provider }
}
pub fn create(&self, i: i32) -> Foo {
Foo { i, s : self.s_provider.get() }
}
}
This is a lot of boilerplate, and can be automated by
using #[factory]
instead
of #[inject]
#[injectable]
impl Foo {
#[factory]
fn create(#[runtime] i: i32, s: String) -> Self {
Self { i, s }
}
}
Runtime parameters needs to be marked with
the #[runtime]
attribute.
FooFactory
will be created by Lockjaw, with a method with the same name as the marked method
taking only runtime parameters.
let foo = component.foo_factory().create(42);
Factory traits
The factory can also be instructed to implement a trait
by using the
implementing
metadata
.
pub trait FooCreator {
fn create(&self, i: i32) -> Foo;
}
#[injectable]
impl Foo {
#[factory(implementing: FooCreator)]
fn create(#[runtime] i: i32, phrase: String) -> Self {
Self { i, phrase }
}
}
The method name and runtime signature must match the trait
method the factory should override.
This is especially useful to bind the factory to a trait
#[binds]
pub fn bind_foo_creator(impl_: FooFactory) -> Cl<dyn FooCreator> {}
Examples
https://github.com/azureblaze/lockjaw/blob/main/tests/injectable_factory.rs https://github.com/azureblaze/lockjaw/blob/main/tests/injectable_factory_implementing.rs