diff options
-rw-r--r-- | Cargo.toml | 7 | ||||
-rw-r--r-- | README.md | 9 | ||||
-rw-r--r-- | examples/basic/animals/cow.rs | 24 | ||||
-rw-r--r-- | examples/basic/animals/human.rs | 20 | ||||
-rw-r--r-- | examples/basic/animals/mod.rs | 1 | ||||
-rw-r--r-- | examples/basic/bootstrap.rs | 7 | ||||
-rw-r--r-- | examples/basic/interfaces/cow.rs | 10 | ||||
-rw-r--r-- | examples/basic/interfaces/mod.rs | 1 | ||||
-rw-r--r-- | examples/factory/bootstrap.rs | 24 | ||||
-rw-r--r-- | examples/factory/interfaces/mod.rs | 1 | ||||
-rw-r--r-- | examples/factory/interfaces/user.rs | 13 | ||||
-rw-r--r-- | examples/factory/main.rs | 47 | ||||
-rw-r--r-- | examples/factory/user.rs | 42 | ||||
-rw-r--r-- | macros/src/lib.rs | 5 | ||||
-rw-r--r-- | src/castable_factory.rs | 4 | ||||
-rw-r--r-- | src/di_container.rs | 21 | ||||
-rw-r--r-- | src/interfaces/any_factory.rs | 3 | ||||
-rw-r--r-- | src/interfaces/mod.rs | 5 | ||||
-rw-r--r-- | src/lib.rs | 6 | ||||
-rw-r--r-- | src/provider.rs | 6 |
20 files changed, 175 insertions, 81 deletions
@@ -8,6 +8,13 @@ repository = "https://git.hampusmat.com/syrette" keywords = ["DI", "dependency-injection", "ioc", "inversion-of-control"] edition = "2021" +[features] +factory = [] + +[[example]] +name = "factory" +required-features = ["factory"] + [dependencies] syrette_macros = { path = "./macros", version = "0.1.0" } linkme = "0.3.0" @@ -13,11 +13,15 @@ From the [syrette Wikipedia article](https://en.wikipedia.org/wiki/Syrette). ## Features - A [dependency injection](https://en.wikipedia.org/wiki/Dependency_injection) container - Autowiring dependencies -- Binding factories - API inspired from the one of [InversifyJS](https://github.com/inversify/InversifyJS) - Helpful error messages - Enforces the use of interface traits +## Optional features +- Binding factories (Rust nightly required) + +To use these features, you must [enable it in Cargo](https://doc.rust-lang.org/cargo/reference/features.html#dependency-features). + ## Motivation Other DI libraries for Rust are either unmaintained ([di](https://crates.io/crates/di) for example), overcomplicated and or bloated ([anthill-di](https://crates.io/crates/anthill-di) for example) @@ -25,9 +29,6 @@ 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 library. -## Notice -Rust nightly is currently required. - ## Example usage ```rust use syrette::{injectable, DIContainer}; diff --git a/examples/basic/animals/cow.rs b/examples/basic/animals/cow.rs deleted file mode 100644 index a75d750..0000000 --- a/examples/basic/animals/cow.rs +++ /dev/null @@ -1,24 +0,0 @@ -use crate::interfaces::cow::ICow; - -pub struct Cow -{ - moo_cnt: i32, -} - -impl Cow -{ - pub fn new(moo_cnt: i32) -> Self - { - Self { moo_cnt } - } -} - -impl ICow for Cow -{ - fn moo(&self) - { - for _ in 0..self.moo_cnt { - println!("Moo"); - } - } -} diff --git a/examples/basic/animals/human.rs b/examples/basic/animals/human.rs index 5bd2f8f..00574a3 100644 --- a/examples/basic/animals/human.rs +++ b/examples/basic/animals/human.rs @@ -1,8 +1,7 @@ use syrette::injectable; -use syrette::ptr::{FactoryPtr, InterfacePtr}; +use syrette::ptr::InterfacePtr; use crate::interfaces::cat::ICat; -use crate::interfaces::cow::{CowFactory, ICow}; use crate::interfaces::dog::IDog; use crate::interfaces::human::IHuman; @@ -10,23 +9,14 @@ pub struct Human { dog: InterfacePtr<dyn IDog>, cat: InterfacePtr<dyn ICat>, - cow_factory: FactoryPtr<CowFactory>, } #[injectable(IHuman)] impl Human { - pub fn new( - dog: InterfacePtr<dyn IDog>, - cat: InterfacePtr<dyn ICat>, - cow_factory: FactoryPtr<CowFactory>, - ) -> Self + pub fn new(dog: InterfacePtr<dyn IDog>, cat: InterfacePtr<dyn ICat>) -> Self { - Self { - dog, - cat, - cow_factory, - } + Self { dog, cat } } } @@ -41,9 +31,5 @@ impl IHuman for Human println!("Hi kitty!"); self.cat.meow(); - - let cow: Box<dyn ICow> = (self.cow_factory)(3); - - cow.moo(); } } diff --git a/examples/basic/animals/mod.rs b/examples/basic/animals/mod.rs index 6511d17..5444978 100644 --- a/examples/basic/animals/mod.rs +++ b/examples/basic/animals/mod.rs @@ -1,4 +1,3 @@ pub mod cat; -pub mod cow; pub mod dog; pub mod human; diff --git a/examples/basic/bootstrap.rs b/examples/basic/bootstrap.rs index a1a7b05..71ef713 100644 --- a/examples/basic/bootstrap.rs +++ b/examples/basic/bootstrap.rs @@ -2,13 +2,11 @@ use syrette::DIContainer; // Concrete implementations use crate::animals::cat::Cat; -use crate::animals::cow::Cow; use crate::animals::dog::Dog; use crate::animals::human::Human; // // Interfaces use crate::interfaces::cat::ICat; -use crate::interfaces::cow::{CowFactory, ICow}; use crate::interfaces::dog::IDog; use crate::interfaces::human::IHuman; @@ -20,10 +18,5 @@ pub fn bootstrap() -> DIContainer di_container.bind::<dyn ICat>().to::<Cat>(); di_container.bind::<dyn IHuman>().to::<Human>(); - di_container.bind::<CowFactory>().to_factory(&|moo_cnt| { - let cow: Box<dyn ICow> = Box::new(Cow::new(moo_cnt)); - cow - }); - di_container } diff --git a/examples/basic/interfaces/cow.rs b/examples/basic/interfaces/cow.rs deleted file mode 100644 index 59ce7b1..0000000 --- a/examples/basic/interfaces/cow.rs +++ /dev/null @@ -1,10 +0,0 @@ -use syrette::factory; -use syrette::interfaces::factory::IFactory; - -pub trait ICow -{ - fn moo(&self); -} - -#[factory] -pub type CowFactory = dyn IFactory<(i32,), dyn ICow>; diff --git a/examples/basic/interfaces/mod.rs b/examples/basic/interfaces/mod.rs index 6511d17..5444978 100644 --- a/examples/basic/interfaces/mod.rs +++ b/examples/basic/interfaces/mod.rs @@ -1,4 +1,3 @@ pub mod cat; -pub mod cow; pub mod dog; pub mod human; diff --git a/examples/factory/bootstrap.rs b/examples/factory/bootstrap.rs new file mode 100644 index 0000000..5086b1a --- /dev/null +++ b/examples/factory/bootstrap.rs @@ -0,0 +1,24 @@ +use syrette::ptr::InterfacePtr; +use syrette::DIContainer; + +// Interfaces +use crate::interfaces::user::{IUser, IUserFactory}; +// +// Concrete implementations +use crate::user::User; + +pub fn bootstrap() -> DIContainer +{ + let mut di_container: DIContainer = DIContainer::new(); + + di_container + .bind::<IUserFactory>() + .to_factory(&|name, date_of_birth, password| { + let user: InterfacePtr<dyn IUser> = + InterfacePtr::new(User::new(name, date_of_birth, password)); + + user + }); + + di_container +} diff --git a/examples/factory/interfaces/mod.rs b/examples/factory/interfaces/mod.rs new file mode 100644 index 0000000..22d12a3 --- /dev/null +++ b/examples/factory/interfaces/mod.rs @@ -0,0 +1 @@ +pub mod user; diff --git a/examples/factory/interfaces/user.rs b/examples/factory/interfaces/user.rs new file mode 100644 index 0000000..70cd632 --- /dev/null +++ b/examples/factory/interfaces/user.rs @@ -0,0 +1,13 @@ +use syrette::factory; +use syrette::interfaces::factory::IFactory; + +pub trait IUser +{ + fn get_name(&self) -> &'static str; + fn get_date_of_birth(&self) -> &'static str; + fn get_password(&self) -> &'static str; +} + +#[factory] +pub type IUserFactory = + dyn IFactory<(&'static str, &'static str, &'static str), dyn IUser>; diff --git a/examples/factory/main.rs b/examples/factory/main.rs new file mode 100644 index 0000000..c659f3e --- /dev/null +++ b/examples/factory/main.rs @@ -0,0 +1,47 @@ +#![deny(clippy::all)] +#![deny(clippy::pedantic)] +#![allow(clippy::module_name_repetitions)] + +mod bootstrap; +mod interfaces; +mod user; + +use bootstrap::bootstrap; +use interfaces::user::IUser; +use interfaces::user::IUserFactory; +use syrette::ptr::FactoryPtr; +use syrette::ptr::InterfacePtr; + +fn add_users( + users: &mut Vec<InterfacePtr<dyn IUser>>, + user_factory: &FactoryPtr<IUserFactory>, +) +{ + users.push(user_factory("Bob", "1983-04-13", "abc1234")); + users.push(user_factory("Anna", "1998-01-20", "IlovemYCat")); + users.push(user_factory("David", "2000-11-05", "12345678")); +} + +fn main() +{ + println!("Hello, world!"); + + let di_container = bootstrap(); + + let user_factory = di_container.get_factory::<IUserFactory>().unwrap(); + + let mut users = Vec::<InterfacePtr<dyn IUser>>::new(); + + add_users(&mut users, &user_factory); + + println!("Printing user information"); + + for user in users { + println!( + "{}, born {}, password is '{}'", + user.get_name(), + user.get_date_of_birth(), + user.get_password() + ); + } +} diff --git a/examples/factory/user.rs b/examples/factory/user.rs new file mode 100644 index 0000000..121ad25 --- /dev/null +++ b/examples/factory/user.rs @@ -0,0 +1,42 @@ +use crate::interfaces::user::IUser; + +pub struct User +{ + name: &'static str, + date_of_birth: &'static str, + password: &'static str, +} + +impl User +{ + pub fn new( + name: &'static str, + date_of_birth: &'static str, + password: &'static str, + ) -> Self + { + Self { + name, + date_of_birth, + password, + } + } +} + +impl IUser for User +{ + fn get_name(&self) -> &'static str + { + self.name.clone() + } + + fn get_date_of_birth(&self) -> &'static str + { + self.date_of_birth.clone() + } + + fn get_password(&self) -> &'static str + { + self.password.clone() + } +} diff --git a/macros/src/lib.rs b/macros/src/lib.rs index ed1a509..73fb2dc 100644 --- a/macros/src/lib.rs +++ b/macros/src/lib.rs @@ -12,7 +12,6 @@ mod injectable_macro_args; mod libs; use declare_interface_args::DeclareInterfaceArgs; -use factory_type_alias::FactoryTypeAlias; use injectable_impl::InjectableImpl; use injectable_macro_args::InjectableMacroArgs; use libs::intertrait_macros::gen_caster::generate_caster; @@ -52,7 +51,7 @@ pub fn injectable(args_stream: TokenStream, impl_stream: TokenStream) -> TokenSt #[proc_macro_attribute] pub fn factory(_: TokenStream, type_alias_stream: TokenStream) -> TokenStream { - let FactoryTypeAlias { + let factory_type_alias::FactoryTypeAlias { type_alias, factory_interface, arg_types, @@ -73,7 +72,7 @@ pub fn factory(_: TokenStream, type_alias_stream: TokenStream) -> TokenStream syrette::castable_factory::CastableFactory< #arg_types, #return_type - > -> syrette::castable_factory::AnyFactory + > -> syrette::interfaces::any_factory::AnyFactory ); } .into() diff --git a/src/castable_factory.rs b/src/castable_factory.rs index 5d582bb..c50456c 100644 --- a/src/castable_factory.rs +++ b/src/castable_factory.rs @@ -1,9 +1,7 @@ +use crate::interfaces::any_factory::AnyFactory; use crate::interfaces::factory::IFactory; -use crate::libs::intertrait::CastFrom; use crate::ptr::InterfacePtr; -pub trait AnyFactory: CastFrom {} - pub struct CastableFactory<Args, ReturnInterface> where Args: 'static, diff --git a/src/di_container.rs b/src/di_container.rs index 6982a10..ae6a851 100644 --- a/src/di_container.rs +++ b/src/di_container.rs @@ -5,14 +5,13 @@ use std::rc::Rc; use error_stack::{Report, ResultExt}; +#[cfg(feature = "factory")] use crate::castable_factory::CastableFactory; use crate::errors::di_container::DIContainerError; -use crate::interfaces::factory::IFactory; use crate::interfaces::injectable::Injectable; use crate::libs::intertrait::cast_box::CastBox; -use crate::libs::intertrait::cast_rc::CastRc; -use crate::provider::{FactoryProvider, IProvider, InjectableTypeProvider, Providable}; -use crate::ptr::{FactoryPtr, InterfacePtr}; +use crate::provider::{IProvider, InjectableTypeProvider, Providable}; +use crate::ptr::InterfacePtr; /// Binding builder for type `Interface` inside a [`DIContainer`]. pub struct BindingBuilder<'di_container_lt, Interface> @@ -51,13 +50,14 @@ where /// Creates a binding of factory type `Interface` to a factory inside of the /// associated [`DIContainer`]. + #[cfg(feature = "factory")] pub fn to_factory<Args, Return>( &mut self, factory_func: &'static dyn Fn<Args, Output = InterfacePtr<Return>>, ) where Args: 'static, Return: 'static + ?Sized, - Interface: IFactory<Args, Return>, + Interface: crate::interfaces::factory::IFactory<Args, Return>, { let interface_typeid = TypeId::of::<Interface>(); @@ -65,7 +65,9 @@ where self.di_container.bindings.insert( interface_typeid, - Rc::new(FactoryProvider::new(FactoryPtr::new(factory_impl))), + Rc::new(crate::provider::FactoryProvider::new( + crate::ptr::FactoryPtr::new(factory_impl), + )), ); } } @@ -197,9 +199,10 @@ impl DIContainer /// - Resolving the binding for `Interface` fails /// - Casting the binding for `Interface` fails /// - The binding for `Interface` is not a factory + #[cfg(feature = "factory")] pub fn get_factory<Interface>( &self, - ) -> error_stack::Result<FactoryPtr<Interface>, DIContainerError> + ) -> error_stack::Result<crate::ptr::FactoryPtr<Interface>, DIContainerError> where Interface: 'static + ?Sized, { @@ -222,6 +225,8 @@ impl DIContainer match binding_providable { Providable::Factory(binding_factory) => { + use crate::libs::intertrait::cast_rc::CastRc; + let factory_box_result = binding_factory.cast::<Interface>(); match factory_box_result { @@ -306,6 +311,7 @@ mod tests } #[test] + #[cfg(feature = "factory")] fn can_bind_to_factory() { trait IUserManager @@ -423,6 +429,7 @@ mod tests } #[test] + #[cfg(feature = "factory")] fn can_get_factory() -> error_stack::Result<(), DIContainerError> { trait IUserManager diff --git a/src/interfaces/any_factory.rs b/src/interfaces/any_factory.rs new file mode 100644 index 0000000..41063e1 --- /dev/null +++ b/src/interfaces/any_factory.rs @@ -0,0 +1,3 @@ +use crate::libs::intertrait::CastFrom; + +pub trait AnyFactory: CastFrom {} diff --git a/src/interfaces/mod.rs b/src/interfaces/mod.rs index 921bb9c..497521e 100644 --- a/src/interfaces/mod.rs +++ b/src/interfaces/mod.rs @@ -1,2 +1,5 @@ -pub mod factory; +pub mod any_factory; pub mod injectable; + +#[cfg(feature = "factory")] +pub mod factory; @@ -1,4 +1,4 @@ -#![feature(unboxed_closures, fn_traits)] +#![cfg_attr(feature = "factory", feature(unboxed_closures, fn_traits))] #![deny(clippy::all)] #![deny(clippy::pedantic)] @@ -6,12 +6,14 @@ //! //! Syrette is a collection of utilities useful for performing dependency injection. -pub mod castable_factory; pub mod di_container; pub mod errors; pub mod interfaces; pub mod ptr; +#[cfg(feature = "factory")] +pub mod castable_factory; + pub use di_container::*; pub use syrette_macros::*; diff --git a/src/provider.rs b/src/provider.rs index 3b7e04c..bd17474 100644 --- a/src/provider.rs +++ b/src/provider.rs @@ -1,8 +1,8 @@ #![allow(clippy::module_name_repetitions)] use std::marker::PhantomData; -use crate::castable_factory::AnyFactory; use crate::errors::injectable::ResolveError; +use crate::interfaces::any_factory::AnyFactory; use crate::interfaces::injectable::Injectable; use crate::ptr::{FactoryPtr, InterfacePtr}; use crate::DIContainer; @@ -12,6 +12,7 @@ extern crate error_stack; pub enum Providable { Injectable(InterfacePtr<dyn Injectable>), + #[allow(dead_code)] Factory(FactoryPtr<dyn AnyFactory>), } @@ -57,11 +58,13 @@ where } } +#[cfg(feature = "factory")] pub struct FactoryProvider { factory: FactoryPtr<dyn AnyFactory>, } +#[cfg(feature = "factory")] impl FactoryProvider { pub fn new(factory: FactoryPtr<dyn AnyFactory>) -> Self @@ -70,6 +73,7 @@ impl FactoryProvider } } +#[cfg(feature = "factory")] impl IProvider for FactoryProvider { fn provide( |