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