12 releases
0.4.4 | Sep 15, 2024 |
---|---|
0.4.3 | Apr 27, 2024 |
0.4.2 | Mar 24, 2024 |
0.3.1 | Dec 8, 2023 |
0.2.0 | Oct 21, 2022 |
#965 in Procedural macros
40 downloads per month
Used in nject
76KB
1.5K
SLoC
nject
Simple zero cost dependency injection library made for rust
Install
Add the following to your Cargo.toml
:
[dependencies]
nject = "0.4"
Why nject
?
- Zero cost: Using this library is equivalent to manually injecting your dependencies as shown in the benchmarks.
- Compile time only: If configured incorrectly,
nject
will fail at compile time.
Use cases
Removes the need to specify dependencies across your modules
use nject::{injectable, provider};
#[injectable]
struct DepOne;
#[injectable]
struct DepTwo {
dep: DepOne,
}
#[injectable]
struct Facade {
dep: DepTwo,
}
#[provider]
struct Provider;
fn main() {
let _facade: Facade = Provider.provide();
}
Works with lifetimes - enables shared dependencies
use nject::{injectable, provider};
struct DepOne;
#[injectable]
struct Facade<'a> {
dep: &'a DepOne,
}
#[provider]
struct Provider {
#[provide]
shared: DepOne,
}
fn main() {
let provider = Provider { shared: DepOne };
let _facade: Facade = provider.provide();
}
Works with dyn traits
use nject::{injectable, provider};
use std::rc::Rc;
trait Greeter {
fn greet(&self);
}
#[injectable]
struct GreeterOne;
impl Greeter for GreeterOne {
fn greet(&self) {
println!("Greeting");
}
}
#[injectable]
struct Facade<'a> {
boxed_dep: Box<dyn Greeter>,
ref_dep: &'a dyn Greeter,
rc_dep: Rc<dyn Greeter>,
}
#[provider]
#[provide(Box<dyn Greeter>, |greeter: GreeterOne| Box::new(greeter))]
struct Provider {
#[provide(dyn Greeter)]
greeter: GreeterOne,
#[provide(Rc<dyn Greeter>, |x| x.clone())]
rc_greeter: Rc<GreeterOne>,
}
fn main() {
let provider = Provider {
greeter: GreeterOne,
rc_greeter: Rc::new(GreeterOne),
};
let _facade: Facade = provider.provide();
}
Works with generics
use nject::{injectable, provider};
#[injectable]
struct DepOne;
#[injectable]
struct Facade<T> {
dep: T,
}
#[provider]
struct Provider;
fn main() {
let _facade: Facade<DepOne> = Provider.provide();
}
Works with generic providers
use nject::{injectable, provider};
trait Greeter {
fn greet(&self);
}
#[injectable]
struct DevGreeter;
impl Greeter for DevGreeter {
fn greet(&self) {
println!("Greeting Dev");
}
}
#[injectable]
struct ProdGreeter;
impl Greeter for ProdGreeter {
fn greet(&self) {
println!("Greeting production");
}
}
#[injectable]
struct Facade<'a> {
dep: &'a dyn Greeter,
}
#[provider]
struct Provider<'a, T: Greeter>(#[provide(dyn Greeter)] &'a T);
fn main() {
let _dev_facade: Facade = Provider(&DevGreeter).provide();
let _prod_facade: Facade = Provider(&ProdGreeter).provide();
}
Easily inject non-injectable dependencies
use nject::{inject, injectable, provider};
#[inject(Self { non_injectable_value: 123 })]
struct InjectableFromInjectAttr {
non_injectable_value: i32,
}
struct NonInjectable {
non_injectable_value: i32,
}
#[inject(|injectable_dep: InjectableFromInjectAttr| Self {
non_injectable_value: injectable_dep.non_injectable_value + 10,
injectable_dep
})]
struct PartiallyInjectable {
non_injectable_value: i32,
injectable_dep: InjectableFromInjectAttr
}
#[injectable]
struct Facade {
dep_from_injected: InjectableFromInjectAttr,
dep_from_partial_inject: PartiallyInjectable,
#[inject(NonInjectable { non_injectable_value: 456 })]
dep_from_inject_attr: NonInjectable,
#[inject(InjectableFromInjectAttr { non_injectable_value: 789 })]
dep_from_inject_attr_override: InjectableFromInjectAttr,
#[inject(|injectable_dep: InjectableFromInjectAttr| PartiallyInjectable {
non_injectable_value: 111,
injectable_dep
})]
dep_from_partial_inject_attr_override: PartiallyInjectable,
}
#[provider]
struct Provider;
fn main() {
let _facade = Provider.provide::<Facade>();
}
Use modules to export internal shared dependencies
use nject::{injectable, provider};
mod sub {
use nject::{injectable, module};
use std::rc::Rc;
trait Greeter {
fn greet(&self) -> &str;
}
#[injectable]
struct GreeterOne;
impl Greeter for GreeterOne {
fn greet(&self) -> &str {
"One"
}
}
#[injectable]
struct InternalType(#[inject(123)] i32); // Not visible outside of module.
struct Ref<T>(Rc<T>);
#[injectable]
pub struct Facade<'a> {
hidden: &'a InternalType,
hidden_dyn: &'a dyn Greeter,
hidden_ref: Ref<InternalType>,
}
#[injectable]
#[module]
pub struct Module {
// Internal shared type exports must be made on fields (not the struct).
#[export]
hidden: InternalType,
#[export(dyn Greeter)]
hidden_dyn: GreeterOne,
#[inject(|x: InternalType| Rc::new(x))]
#[export(Ref<InternalType>, |x| Ref(x.clone()))]
hidden_rc: Rc<InternalType>,
}
}
#[injectable]
#[provider]
struct Provider {
#[import]
sub_mod: sub::Module,
}
fn main() {
#[provider]
struct InitProvider;
let provider = InitProvider.provide::<Provider>();
let _facade = provider.provide::<sub::Facade>();
}
Limitations
- Internal dependencies can only be exported by a single module.
- Generic parameters are not supported on modules.
Use modules to export public dependencies
use nject::{injectable, provider};
mod sub {
use nject::{injectable, module};
use std::boxed::Box;
use std::rc::Rc;
pub trait Greeter {
fn greet(&self) -> &str;
}
#[injectable]
struct GreeterOne;
impl Greeter for GreeterOne {
fn greet(&self) -> &str {
"One"
}
}
#[injectable]
pub struct Facade<'a> {
public_box: Box<dyn Greeter>,
public_rc: Rc<dyn Greeter>,
public_i32: &'a i32,
}
#[injectable]
// The absolute public path to access the module.
// If no path is given, the struct name will be used and must be unique across all modules.
// Keywords like `crate` and `Self` will be substituted accordingly.
#[module(crate::sub::Self)]
// Public type exports must be made on the struct (not the fields).
// To prevent name collisions, use absolute paths in types.
#[export(std::boxed::Box<dyn crate::sub::Greeter>, |x: GreeterOne| Box::new(x))]
#[export(std::rc::Rc<dyn crate::sub::Greeter>, self.public.clone())]
#[export(&'prov i32, &123)]
pub struct Module {
#[inject(|x: GreeterOne| Rc::new(x))]
public: Rc<dyn Greeter>,
}
}
#[injectable]
#[provider]
struct Provider {
#[import]
// To import module public exports, use the absolute path given in its definition.
sub_mod: crate::sub::Module,
}
fn main() {
#[provider]
struct InitProvider;
let provider = InitProvider.provide::<Provider>();
let _facade = provider.provide::<sub::Facade>();
}
Limitations
- Public exports are discovered as macros expand. Therefore, modules must expand before their use in any providers.
- This limitation is only applicable if both module and provider are defined in the same crate.
- Requires
cargo
to build. Runcargo clean -p nject-macro
to clean the cache if it ever gets corrupted. - Generic parameters are not supported on modules.
Use scopes to scope dependencies
use nject::{injectable, module, provider};
#[injectable]
struct ModuleDep;
#[injectable]
#[module]
struct ScopeModule {
#[export]
module_dep: ModuleDep,
}
#[injectable]
struct RootDep;
#[injectable]
struct ScopeDep;
#[injectable]
struct ScopeFacade<'a> {
root_dep: &'a RootDep,
scope_dep: &'a ScopeDep,
scope_module_dep: &'a ModuleDep,
}
#[injectable]
#[provider]
#[scope(ScopeDep)]
#[scope(#[import] ScopeModule)]
#[scope(other: #[arg] &'scope ScopeDep)]
#[scope(other: #[arg] &'scope ModuleDep)]
struct Provider(#[provide] RootDep);
fn main() {
#[provider]
struct InitProvider;
let provider = InitProvider.provide::<Provider>();
let scope = provider.scope();
let scope_facade = scope.provide::<ScopeFacade>();
let other_scope = provider.other_scope(scope_facade.scope_dep, scope_facade.scope_module_dep);
let _other_scope_facade = other_scope.provide::<ScopeFacade>();
}
Inject providers for post-creation value injection
use nject::{injectable, provider};
#[injectable]
struct Dep(#[inject(123)] i32);
#[injectable]
struct Factory<'a> {
dep_provider: &'a dyn nject::Provider<'a, Dep>,
}
impl<'a> Factory<'a> {
fn create_dep(&self) -> Dep {
self.dep_provider.provide()
}
}
#[provider]
struct Provider;
fn main() {
let factory = Provider.provide::<Factory>();
let _dep = factory.create_dep();
}
Examples
You can look into the axum/actix example for a Web API use case or into the Leptos example for a Web App.
Credits
- Syn - MIT or Apache-2.0
- Quasi-Quoting - MIT or Apache-2.0
- Rust - MIT or Apache-2.0
Dependencies
~215–650KB
~15K SLoC