Teloc is simple, compile-time DI framework for Rust inspired by C# Dependency Injection Framework.
Dependency injection (DI) is a technique in which an object receives other objects that it depends on. These other objects are called dependencies. In the typical "using" relationship the receiving object is called a client and the passed (that is, "injected") object is called a service. The code that passes the service to the client can be many kinds of things and is called the injector. Instead of the client specifying which service it will use, the injector tells the client what service to use. The "injection" refers to the passing of a dependency (a service) into the object (a client) that would use it.
There are one type can be provider of services: ServiceProvider
. It used as store for dependencies with
Instance
and Singleton
lifetimes, and for declaring all dependencies using .add_*()
methods. It can be forked to
create a local scope with local instances.
There are four lifetimes for dependencies:
Transient
. Service will be created when resolves. Can depend on dependencies with anything lifetime.Singleton
. Service will be created once at ServiceProvider
when it resolved (lazy). Can depend on dependencies
with anything lifetime. Cannot depend on services from forked ServiceProvider
instances.Instance
. Dependency was created outside of ServiceProvider
and can be used by any other dependency.How to work:
#[inject]
macro on its.ServiceProvider
object.ServiceProvider::add_*
methods.ServiceProvider
if you need to create local scope..resolve()
method.Example:
use teloc::*;
// Declare your structs
struct ConstService {
number: i32,
}
// #[inject] macro is indicate that dependency can be constructed using this function
#[inject]
impl ConstService {
pub fn new(number: i32) -> Self {
ConstService { number }
}
}
// Derive macro can be used when all fields implement `Dependency` trait, but
// we recommend using the #[inject] macro it in production code instead.
#[derive(Dependency)]
struct Controller {
number_service: ConstService,
}
fn main() {
// Create `ServiceProvider` struct that store itself all dependencies
let sp = ServiceProvider::new()
// Add dependency with `Singleton` lifetime. More about lifetimes see above.
.add_singleton::<ConstService>()
// Add dependency with `Transient` lifetime. More about lifetimes see above.
.add_transient::<Controller>();
// Fork `ServiceProvider`. It creates a new `ServiceProvider` which will have
// access to the dependencies from parent `ServiceProvider`.
let scope = sp
// .fork() method creates a local mutable scope with self parent immutable `ServiceProvider`.
.fork()
// Add an instance of `i32` that will be used when `ConstService` will be initialized.
.add_instance(10);
// Get dependency from `ServiceProvider`
let controller: Controller = scope.resolve();
assert_eq!(controller.number_service.number, 10);
}
For documentation see page on docs.rs.
For more examples see examples folder or tests folder.
Feature | teloc | shaku | waiter_di |
Compile-time checks | Yes | Yes | Yes |
Can be used without dyn traits | Yes | Yes | Yes |
Many service providers in one app | Yes | Yes | No |
Different lifetimes | Yes | Yes | No |
Sometimes teloc
can give strange large errors. But no panic! You can define your problem by read the manual of reading errors.
This section contains pro tips that you might want to use when working with the library.
ServiceProvider
It will be useful when you want to store an instance of ServiceProvider
in a struct or return from a function or
pass as an argument.
What you need:
ServiceProvider
initialization: let () = service_provider;
.teloc::ServiceProvider<...>
.type ConcreteSP = /*compiler output*/;
.ConcreteSP
when you want write ServiceProvider
instance type.ServiceProvider
initialization repeat steps 1-4.Licensed under either of Apache License, Version 2.0 or MIT license at your option.
Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in this project by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.