Requesting objects

In the last chapter we manually wrote an object factory:

struct Factory {}

impl Factory {
    pub fn create_foo(&self) -> Foo {
        Foo::new()
    }

    pub fn create_bar(&self) -> Bar {
        Bar::new(self.create_foo())
    }
}

#[test]
fn test() {
    let factory = Factory {};

    let _foo = factory.create_foo();
    let _bar = factory.create_bar();
}

Now we will ask Lockjaw to automatically generate it. A factory generating objects using a certain dependency graph is called a Component in Dagger terminology.

Defining the component

#[component]
trait MyComponent {
    fn create_foo(&self) -> Foo;

    fn create_bar(&self) -> Bar;
}

Comparing to the manual factory there are not a lot of changes. Since Lockjaw Cannot generate the implementation immediately, we use a trait instead of a struct as the interface has to be abstract. The trait is then annotated with the #[component] attribute that instructs Lockjaw to generate the implementation. Lockjaw is able to identify all #[inject] bindings automatically.

For every type we wish to be able to directly create from the component, a method returning the type should be added to the trait. Like the manual factory, the method should take &self (and nothing else) so it may further use the component to create the dependencies it needs. The name of the method does not really matter, but since you are going to call it later you'd probably want something sensible.

Note that if a type is not directly needed, the trait does not need a method for it. For example while Bar needs Foo, if we are never going to use Foo in main() we can delete the create_foo() method. The trait methods does not affect Lockjaw internal generation, it only declares what needs to be publicly provided.

Creating and using the component.

Since create_foo(&self) is a method, we need to create the component to be able to call it. Lockjaw generates a static build() method on the trait that can be used to create the component.

#[test]
fn test() {
    let component: Box<dyn MyComponent> = <dyn MyComponent>::build();

    let _foo = component.create_foo();
    let _bar = component.create_bar();
}

build() returns Box<dyn COMPONENT>. Most IDE today does not understand symbols generated by procedural macros, so you might want to manually hint the type in the let expression.

Once the component is created, the trait methods can be used to create the objects.

Source of this chapter