## Syrette [![Latest Version](https://img.shields.io/crates/v/syrette)](https://crates.io/crates/syrette) [![Documentation](https://img.shields.io/badge/docs.rs-syrette-blueviolet)](https://docs.rs/syrette) [![Build](https://img.shields.io/circleci/build/github/HampusMat/Syrette/master)](https://app.circleci.com/pipelines/github/HampusMat/Syrette) [![Coverage](https://img.shields.io/codecov/c/github/HampusMat/Syrette)](https://app.codecov.io/gh/HampusMat/Syrette) [![Rust](https://img.shields.io/badge/rust-1.62.1%2B-informational)](#rust-version-requirements) The convenient dependency injection & inversion of control framework for Rust.
Currently, This crate does not work on nightly versions of Rust. This is because Nightly versions uses rust-lld. See [Linkme issue #94](https://github.com/dtolnay/linkme/issues/94). A temporary fix for this is to build with `RUSTFLAGS="-C link-args=-znostart-stop-gc"`
## Namesake From the [syrette Wikipedia article](https://en.wikipedia.org/wiki/Syrette). > A syrette is a device for injecting liquid through a needle. > It is similar to a syringe except that it has a closed flexible > tube (like that typically used for toothpaste) instead of a rigid tube and piston. ## Features - A [dependency injection](https://en.wikipedia.org/wiki/Dependency_injection) and [inversion of control](https://en.wikipedia.org/wiki/Inversion_of_control) container - Autowiring dependencies - API inspired from the one of [InversifyJS](https://github.com/inversify/InversifyJS) - Helpful error messages - Supports generic implementations & generic interface traits - Binding singletons - Injection of third-party structs & traits - Named bindings - Async factories ## Optional features - `factory`. Binding factories (Rust nightly required) - `prevent-circular`. Detection and prevention of circular dependencies. (Enabled by default) - `async`. Asynchronous support To use these features, you must [enable it in Cargo](https://doc.rust-lang.org/cargo/reference/features.html#dependency-features). ## Why inversion of control & dependency injection? The reason for practing IoC and DI is to write modular & loosely coupled applications. This is what we're trying to avoid: ```rust impl Foo { /// ❌ Bad. Foo knows the construction details of Bar. pub fn new() -> Self { Self { bar: Bar::new() } } ``` The following is better: ```rust impl Foo /// ✅ Better. Foo is unaware of how Bar is constructed. pub fn new(bar: Bar) -> Self { Self { bar } } } ``` This will however grow quite tiresome sooner or later when you have a large codebase with many dependencies and dependencies of those and so on. Because you will have to specify the dependencies someplace ```rust let foobar = Foobar::new( Foo:new( Woof::new(), Meow::new()), Bar::new( Something::new(), SomethingElse::new(), SomethingMore::new() ) ) ``` This is where Syrette comes in. ## Motivation Other DI & IoC libraries for Rust are either unmaintained ([di](https://crates.io/crates/di) for example), overcomplicated and requires Rust nightly for all functionality ([anthill-di](https://crates.io/crates/anthill-di) for example) or has a weird API ([teloc](https://crates.io/crates/teloc) for example). The goal of Syrette is to be a simple, useful, convenient and familiar DI & IoC library. ## Example usage ```rust use std::error::Error; use syrette::injectable; use syrette::DIContainer; use syrette::ptr::TransientPtr; trait IWeapon { fn deal_damage(&self, damage: i32); } struct Sword {} #[injectable(IWeapon)] impl Sword { fn new() -> Self { Self {} } } impl IWeapon for Sword { fn deal_damage(&self, damage: i32) { println!("Sword dealt {} damage!", damage); } } trait IWarrior { fn fight(&self); } struct Warrior { weapon: TransientPtr, } #[injectable(IWarrior)] impl Warrior { fn new(weapon: TransientPtr) -> Self { Self { weapon } } } impl IWarrior for Warrior { fn fight(&self) { self.weapon.deal_damage(30); } } fn main() -> Result<(), Box> { let mut di_container = DIContainer::new(); di_container.bind::().to::()?; di_container.bind::().to::()?; let warrior = di_container.get::()?.transient()?; warrior.fight(); println!("Warrior has fighted"); Ok(()) } ``` For more examples see the [examples folder](https://git.hampusmat.com/syrette/tree/examples). ## Terminology **Transient**
A type or trait that is unique to owner. **Singleton**
A type that only has a single instance. The opposite of transient. Generally discouraged. **Interface**
A type or trait that represents a type (itself in the case of it being a type). **Factory**
A function that creates new instances of a specific type or trait. ## Rust version requirements Syrette requires Rust >= 1.62.1 to work. This is mainly due to the dependency on [Linkme](https://crates.io/crates/linkme). ## Todo - Add support for generic factories ## Contributing You can reach out by joining the [mailing list](https://lists.hampusmat.com/postorius/lists/syrette.lists.hampusmat.com/). This is the place to submit patches, feature requests and to report bugs.