Coi provides an easy to use dependency injection framework. Currently, this crate provides the following:
coi::Inject
(trait) - a marker trait that indicates a trait or struct is injectable.coi::Provide
- a trait that indicates a struct is capable of providing a specific implementation of some injectable trait. This is generated for you if you usecoi::Inject
(derive), but can also be written manually.coi::Container
- a container to manage the lifetime of all dependencies. This is still in its early stages, and currently only supports objects that are recreated with each request tocoi::Container::resolve
.coi::ContainerBuilder
- a builder for the above container to simplify construction and guarantee immutability after construction.
How this crate works
For any trait you wish to abstract over, have it inherit the Inject
trait. For structs, impl
Inject
for that struct, e.g.
# use Inject;
;
Then, in order to register the injectable item with the coi::ContainerBuilder
, you also
need a struct that impls Provide<Output = T>
where T
is your trait or struct. Provide
exposes a provide
fn that takes &self
and &Container
. When manually implementing Provide
you must resolve all dependencies with container
. Here's an example below:
# use async_trait;
# use ;
# use Arc;
#
#
;
The dependency "dependency"
above of course needs to be registered in order for the call
to resolve
to not error out:
# use async_trait;
# use ;
# use Arc;
#
#
#
#
#
#
#
#
# ;
#
#
#
;
;
async move ;
In general, you usually won't want to write all of that. You would instead want to use the
procedural macro (see example at the bottom).
The detailed docs for that are at coi::Inject
(derive)
Example
use ;
use Arc;
// Mark injectable traits by inheriting the `Inject` trait.
// For structs that will provide the implementation of an injectable trait, derive `Inject`
// and specify which expr will be used to inject which trait. The method can be any path.
// The arguments for the method are derived from fields marked with the attribute `#[inject]`
// (See Impl2 below).
// Currently, only one trait can be provided, but this will likely be expanded on in future
// versions of this crate.
;
// Don't forget to actually implement the trait ;).
// Mark injectable traits by inheriting the `Inject` trait.
// For structs that will provide the implementation of an injectable trait, derive `Inject`
// and specify which method will be used to inject which trait. The arguments for the method
// are derived from fields marked with the attribute `#[inject]`, so the parameter name must
// match a field name.
// Implement the provider method
// Again, don't forget to actually implement the trait ;).
// You might note that Container::resolve is async. This is to allow any provider to be async
// and since we don't know from the resolution perspective whether any provider will need to be
// async, they all have to be. This might be configurable through feature flags in a future
// version of the library.
async move ;