Attribute Macro lockjaw::injectable_attributes::factory[][src]

#[factory]
Expand description

Generate a factory that can construct the #[injectable] with runtime arguments in addition to injected fields.

Parameters in the method can be annotated with #[runtime], which will be requested by the factory during runtime when creating the struct. Unannotated parameters will be provided with the dependency graph. The factory prepares the arguments, and calls the #[factory] method.

An injectable can only have one method annotated with either #[inject] or #[factory]. The method must be static, and must return an instance of the struct.

Consider using Provider instead if there are no runtime parameters, and multiple instances of the struct needs to be created at runtime.


struct MyModule;

#[module]
impl MyModule {
    #[provides]
    pub fn provide_string(&self) -> String {
        "helloworld".to_owned()
    }
}

pub struct Foo {
    pub i: i32,
    pub phrase: String,
}

#[injectable]
impl Foo {
    #[factory]
    fn create(#[runtime] i: i32, phrase: String) -> Self {
        Self { i, phrase }
    }
}

#[component(modules: MyModule)]
pub trait MyComponent {
    fn foo_factory(&self) -> FooFactory;
}

pub fn main() {
    let component: Box<dyn MyComponent> = <dyn MyComponent>::build();

    let foo = component.foo_factory().create(42);

    assert_eq!(foo.i, 42);
    assert_eq!(foo.phrase, "helloworld");
}

epilogue!();

Parameter attributes

Additional attributes can be added to the parameter to affect how the method behaves.

Parameter attributes are added before the parameter name, for example:

pub fn foo(#[attribute] param1: ParamType)

#[qualified]

Designates a qualifier to the parameter type, so a seperated binding of the same type can be requested.

#[runtime]

Denotes the parameter must be passed by the caller when the factory method is called, instead of being provided by the dependency graph.

The parameter will become a part of the generated factory method’s parameter, in the same order they are declared. Parameters without #[runtime] are stripped from the generated factory method.

Generated code

For a struct Foo with a [factory] method called create_foo:

struct Foo {...}

#[injectable]
impl Foo {
   #[factory]
   pub fn create_foo(#[runtime] runtime_1: Type1, injected: Injected) -> Foo {
      ...
   }
}

The struct FooFactory<'component> will be generated at the same module.

struct FooFactory<'component> { ... }

impl FooFactory<'_> {
   pub fn create_foo(&self, runtime_1 : Type1) -> Foo {
      Foo::create_foo(runtime_1, self.injected.get())
   }
}

The factory contains a method with the same name as the #[factory] method. The factory can implement a trait instead by using the implementing metadata.

The factory depend on bindings from the component, hence cannot outlive it.

FooFactory has private visibility by default, which can be overridden by using the visibility metadata.

Metadata

Injectable factories accept additional metadata in the form of #[factory(key=value, key2=value)].

implementing

Optional path to a trait which the factory will implement, instead of adding a method to the factory struct.

For a struct Foo , the factory trait should have a method with the signature of fn create_foo(&self, runtime_parameters, ...) -> Foo. The name of the #[factory] method should match the trait method.

pub struct Foo {
    pub i: i32,
    pub phrase: String,
}

pub trait FooCreator {
  fn create_foo(&self, i: i32) -> Foo;
}

#[injectable]
impl Foo {
    #[factory(implementing: FooCreator)]
    fn create_foo(#[runtime] i: i32, phrase: String) -> Self {
        Self { i, phrase }
    }
}


struct MyModule;

#[module]
impl MyModule {
    #[provides]
    pub fn provide_string(&self) -> String {
        "helloworld".to_owned()
    }
    
    #[binds]
    pub fn bind_foo_creator(impl_: FooFactory) -> Cl<dyn FooCreator> {}
}

#[component(modules: MyModule)]
pub trait MyComponent {
    fn foo_creator(&self) -> Cl<dyn FooCreator>;
}

pub fn main() {
    let component: Box<dyn MyComponent> = <dyn MyComponent>::build();

    let foo = component.foo_creator().create_foo(42);

    assert_eq!(foo.i, 42);
    assert_eq!(foo.phrase, "helloworld");
}

epilogue!();

visibility

Optional string specifying the visibility of the generated factory. The string must conform to the rust visibility syntax, e.g. "pub", "pub(crate)", "pub(super)", or "pub(in path::to::mod)"

The factory is private by default.