diff options
author | HampusM <hampus@hampusmat.com> | 2022-09-12 20:22:13 +0200 |
---|---|---|
committer | HampusM <hampus@hampusmat.com> | 2022-09-17 14:33:15 +0200 |
commit | c1e682c25c24be3174d44ceb95b0537c38299d0c (patch) | |
tree | 6e59f37e1b98e68fad2e3e2fe4a428ac97fcf8b4 | |
parent | e8e48906a3899e71c9c9d86a3d4528cb7d17e5b9 (diff) |
feat!: allow factories access to DI container
BREAKING CHANGE: Factory types should now be written with the Fn trait instead of the IFactory trait and the to_factory & to_default_factory methods of BindingBuilder now expect a function returning a factory function
-rw-r--r-- | examples/basic/bootstrap.rs | 3 | ||||
-rw-r--r-- | examples/factory/bootstrap.rs | 11 | ||||
-rw-r--r-- | examples/factory/interfaces/user.rs | 4 | ||||
-rw-r--r-- | examples/generics/bootstrap.rs | 3 | ||||
-rw-r--r-- | examples/unbound/bootstrap.rs | 3 | ||||
-rw-r--r-- | examples/with-3rd-party/bootstrap.rs | 5 | ||||
-rw-r--r-- | macros/src/factory_type_alias.rs | 75 | ||||
-rw-r--r-- | macros/src/fn_trait.rs | 68 | ||||
-rw-r--r-- | macros/src/lib.rs | 26 | ||||
-rw-r--r-- | src/di_container.rs | 173 | ||||
-rw-r--r-- | src/provider/blocking.rs | 17 |
11 files changed, 233 insertions, 155 deletions
diff --git a/examples/basic/bootstrap.rs b/examples/basic/bootstrap.rs index 30f6df3..2c45676 100644 --- a/examples/basic/bootstrap.rs +++ b/examples/basic/bootstrap.rs @@ -3,12 +3,9 @@ use std::rc::Rc; use syrette::DIContainer; -// Concrete implementations use crate::animals::cat::Cat; use crate::animals::dog::Dog; use crate::animals::human::Human; -// -// Interfaces use crate::interfaces::cat::ICat; use crate::interfaces::dog::IDog; use crate::interfaces::human::IHuman; diff --git a/examples/factory/bootstrap.rs b/examples/factory/bootstrap.rs index 19fad81..f8bef6e 100644 --- a/examples/factory/bootstrap.rs +++ b/examples/factory/bootstrap.rs @@ -4,11 +4,8 @@ use std::rc::Rc; use syrette::ptr::TransientPtr; use syrette::DIContainer; -// Interfaces use crate::interfaces::user::{IUser, IUserFactory}; use crate::interfaces::user_manager::IUserManager; -// -// Concrete implementations use crate::user::User; use crate::user_manager::UserManager; @@ -20,14 +17,14 @@ pub fn bootstrap() -> Result<Rc<DIContainer>, Box<dyn Error>> .bind::<dyn IUserManager>() .to::<UserManager>()?; - di_container.bind::<IUserFactory>().to_factory( - &|name, date_of_birth, password| { + di_container.bind::<IUserFactory>().to_factory(&|_| { + Box::new(move |name, date_of_birth, password| { let user: TransientPtr<dyn IUser> = TransientPtr::new(User::new(name, date_of_birth, password)); user - }, - )?; + }) + })?; Ok(di_container) } diff --git a/examples/factory/interfaces/user.rs b/examples/factory/interfaces/user.rs index 70cd632..aafd0cb 100644 --- a/examples/factory/interfaces/user.rs +++ b/examples/factory/interfaces/user.rs @@ -1,5 +1,5 @@ use syrette::factory; -use syrette::interfaces::factory::IFactory; +use syrette::ptr::TransientPtr; pub trait IUser { @@ -10,4 +10,4 @@ pub trait IUser #[factory] pub type IUserFactory = - dyn IFactory<(&'static str, &'static str, &'static str), dyn IUser>; + dyn Fn(&'static str, &'static str, &'static str) -> TransientPtr<dyn IUser>; diff --git a/examples/generics/bootstrap.rs b/examples/generics/bootstrap.rs index 752a39b..98d03db 100644 --- a/examples/generics/bootstrap.rs +++ b/examples/generics/bootstrap.rs @@ -2,10 +2,7 @@ use std::rc::Rc; use syrette::{di_container_bind, DIContainer}; -// Interfaces use crate::interfaces::printer::IPrinter; -// -// Implementations use crate::printer::Printer; pub fn bootstrap() -> Rc<DIContainer> diff --git a/examples/unbound/bootstrap.rs b/examples/unbound/bootstrap.rs index 8df6678..04643dc 100644 --- a/examples/unbound/bootstrap.rs +++ b/examples/unbound/bootstrap.rs @@ -3,12 +3,9 @@ use std::rc::Rc; use anyhow::Result; use syrette::DIContainer; -// Concrete implementations use crate::animal_store::AnimalStore; use crate::animals::dog::Dog; use crate::animals::human::Human; -// -// Interfaces use crate::interfaces::animal_store::IAnimalStore; use crate::interfaces::dog::IDog; use crate::interfaces::human::IHuman; diff --git a/examples/with-3rd-party/bootstrap.rs b/examples/with-3rd-party/bootstrap.rs index a4bd84a..c5512c4 100644 --- a/examples/with-3rd-party/bootstrap.rs +++ b/examples/with-3rd-party/bootstrap.rs @@ -5,10 +5,7 @@ use syrette::ptr::TransientPtr; use syrette::{declare_default_factory, DIContainer}; use third_party_lib::Shuriken; -// Interfaces use crate::interfaces::ninja::INinja; -// -// Concrete implementations use crate::ninja::Ninja; declare_default_factory!(Shuriken); @@ -21,7 +18,7 @@ pub fn bootstrap() -> Result<Rc<DIContainer>, Box<dyn Error>> di_container .bind::<Shuriken>() - .to_default_factory(&|| TransientPtr::new(Shuriken::new()))?; + .to_default_factory(&|_| TransientPtr::new(Shuriken::new()))?; Ok(di_container) } diff --git a/macros/src/factory_type_alias.rs b/macros/src/factory_type_alias.rs index 8ea7baa..64afe57 100644 --- a/macros/src/factory_type_alias.rs +++ b/macros/src/factory_type_alias.rs @@ -1,17 +1,20 @@ +use quote::ToTokens; use syn::parse::{Parse, ParseStream}; -use syn::{GenericArgument, ItemType, Path, Type, TypeParamBound, TypeTuple}; +use syn::punctuated::Punctuated; +use syn::{parse, ItemType, Token, Type}; + +use crate::fn_trait::FnTrait; pub struct FactoryTypeAlias { pub type_alias: ItemType, - pub factory_interface: Path, - pub arg_types: TypeTuple, + pub factory_interface: FnTrait, + pub arg_types: Punctuated<Type, Token![,]>, pub return_type: Type, } impl Parse for FactoryTypeAlias { - #[allow(clippy::match_wildcard_for_single_variants)] fn parse(input: ParseStream) -> syn::Result<Self> { let type_alias = match input.parse::<ItemType>() { @@ -19,66 +22,14 @@ impl Parse for FactoryTypeAlias Err(_) => Err(input.error("Expected a type alias")), }?; - let aliased_trait = match &type_alias.ty.as_ref() { - Type::TraitObject(alias_type) => Ok(alias_type), - &_ => Err(input.error("Expected the aliased type to be a trait")), - }?; - - if aliased_trait.bounds.len() != 1 { - return Err(input.error("Expected the aliased trait to have a single bound.")); - } - - let bound_path = &match aliased_trait.bounds.first().unwrap() { - TypeParamBound::Trait(trait_bound) => Ok(trait_bound), - &_ => { - Err(input.error("Expected the bound of the aliased trait to be a trait")) - } - }? - .path; - - if bound_path.segments.is_empty() - || bound_path.segments.last().unwrap().ident != "IFactory" - { - return Err(input - .error("Expected the bound of the aliased trait to be 'dyn IFactory'")); - } - - let angle_bracketed_args = match &bound_path.segments.last().unwrap().arguments { - syn::PathArguments::AngleBracketed(angle_bracketed_args) => { - Ok(angle_bracketed_args) - } - &_ => { - Err(input.error("Expected angle bracketed arguments for 'dyn IFactory'")) - } - }?; - - let arg_types = match &angle_bracketed_args.args[0] { - GenericArgument::Type(arg_types_type) => match arg_types_type { - Type::Tuple(arg_types) => Ok(arg_types), - &_ => Err(input.error(concat!( - "Expected the first angle bracketed argument ", - "of 'dyn IFactory' to be a type tuple" - ))), - }, - &_ => Err(input.error(concat!( - "Expected the first angle bracketed argument ", - "of 'dyn IFactory' to be a type" - ))), - }?; - - let return_type = match &angle_bracketed_args.args[1] { - GenericArgument::Type(arg_type) => Ok(arg_type), - &_ => Err(input.error(concat!( - "Expected the second angle bracketed argument ", - "of 'dyn IFactory' to be a type" - ))), - }?; + let aliased_fn_trait = + parse::<FnTrait>(type_alias.ty.as_ref().to_token_stream().into())?; Ok(Self { - type_alias: type_alias.clone(), - factory_interface: bound_path.clone(), - arg_types: arg_types.clone(), - return_type: return_type.clone(), + type_alias, + factory_interface: aliased_fn_trait.clone(), + arg_types: aliased_fn_trait.inputs, + return_type: aliased_fn_trait.output, }) } } diff --git a/macros/src/fn_trait.rs b/macros/src/fn_trait.rs new file mode 100644 index 0000000..f9b3514 --- /dev/null +++ b/macros/src/fn_trait.rs @@ -0,0 +1,68 @@ +use quote::ToTokens; +use syn::parse::Parse; +use syn::punctuated::Punctuated; +use syn::token::Paren; +use syn::{parenthesized, Ident, Token, Type}; + +/// A function trait. `dyn Fn(u32) -> String` +#[derive(Debug, Clone)] +pub struct FnTrait +{ + pub dyn_token: Token![dyn], + pub trait_ident: Ident, + pub paren_token: Paren, + pub inputs: Punctuated<Type, Token![,]>, + pub r_arrow_token: Token![->], + pub output: Type, +} + +impl Parse for FnTrait +{ + fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> + { + let dyn_token = input.parse::<Token![dyn]>()?; + + let trait_ident = input.parse::<Ident>()?; + + if trait_ident.to_string().as_str() != "Fn" { + return Err(syn::Error::new(trait_ident.span(), "Expected 'Fn'")); + } + + let content; + + let paren_token = parenthesized!(content in input); + + let inputs = content.parse_terminated(Type::parse)?; + + let r_arrow_token = input.parse::<Token![->]>()?; + + let output = input.parse::<Type>()?; + + Ok(Self { + dyn_token, + trait_ident, + paren_token, + inputs, + r_arrow_token, + output, + }) + } +} + +impl ToTokens for FnTrait +{ + fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) + { + self.dyn_token.to_tokens(tokens); + + self.trait_ident.to_tokens(tokens); + + self.paren_token.surround(tokens, |tokens_inner| { + self.inputs.to_tokens(tokens_inner); + }); + + self.r_arrow_token.to_tokens(tokens); + + self.output.to_tokens(tokens); + } +} diff --git a/macros/src/lib.rs b/macros/src/lib.rs index 25f3c5c..4c815db 100644 --- a/macros/src/lib.rs +++ b/macros/src/lib.rs @@ -13,6 +13,7 @@ mod declare_interface_args; mod dependency; mod factory_macro_args; mod factory_type_alias; +mod fn_trait; mod injectable_impl; mod injectable_macro_args; mod libs; @@ -166,6 +167,7 @@ pub fn injectable(args_stream: TokenStream, impl_stream: TokenStream) -> TokenSt /// ``` /// # use syrette::factory; /// # use syrette::interfaces::factory::IFactory; +/// # use syrette::ptr::TransientPtr; /// # /// # trait IConfigurator {} /// # @@ -182,7 +184,7 @@ pub fn injectable(args_stream: TokenStream, impl_stream: TokenStream) -> TokenSt /// # impl IConfigurator for Configurator {} /// # /// #[factory] -/// type IConfiguratorFactory = dyn IFactory<(Vec<String>,), dyn IConfigurator>; +/// type IConfiguratorFactory = dyn Fn(Vec<String>) -> TransientPtr<dyn IConfigurator>; /// ``` #[proc_macro_attribute] #[cfg(feature = "factory")] @@ -226,15 +228,18 @@ pub fn factory(args_stream: TokenStream, type_alias_stream: TokenStream) -> Toke quote! { syrette::declare_interface!( syrette::castable_factory::blocking::CastableFactory< - #arg_types, - #return_type - > -> #factory_interface + (std::rc::Rc<syrette::di_container::DIContainer>,), + #factory_interface + > -> syrette::interfaces::factory::IFactory< + (std::rc::Rc<syrette::di_container::DIContainer>,), + #factory_interface + > ); syrette::declare_interface!( syrette::castable_factory::blocking::CastableFactory< - #arg_types, - #return_type + (std::rc::Rc<syrette::di_container::DIContainer>,), + #factory_interface > -> syrette::interfaces::any_factory::AnyFactory ); } @@ -315,14 +320,17 @@ pub fn declare_default_factory(args_stream: TokenStream) -> TokenStream quote! { syrette::declare_interface!( syrette::castable_factory::blocking::CastableFactory< - (), + (std::rc::Rc<syrette::di_container::DIContainer>,), #interface, - > -> syrette::interfaces::factory::IFactory<(), #interface> + > -> syrette::interfaces::factory::IFactory< + (std::rc::Rc<syrette::di_container::DIContainer>,), + #interface + > ); syrette::declare_interface!( syrette::castable_factory::blocking::CastableFactory< - (), + (std::rc::Rc<syrette::di_container::DIContainer>,), #interface, > -> syrette::interfaces::any_factory::AnyFactory ); diff --git a/src/di_container.rs b/src/di_container.rs index ddd3a4f..532a905 100644 --- a/src/di_container.rs +++ b/src/di_container.rs @@ -249,27 +249,36 @@ where #[cfg(feature = "factory")] pub fn to_factory<Args, Return>( &self, - factory_func: &'static dyn Fn<Args, Output = crate::ptr::TransientPtr<Return>>, + factory_func: &'static dyn Fn< + (std::rc::Rc<DIContainer>,), + Output = Box<dyn Fn<Args, Output = crate::ptr::TransientPtr<Return>>>, + >, ) -> Result<BindingWhenConfigurator<Interface>, BindingBuilderError> where Args: 'static, Return: 'static + ?Sized, - Interface: crate::interfaces::factory::IFactory<Args, Return>, + Interface: Fn<Args, Output = crate::ptr::TransientPtr<Return>>, { - let mut bindings_mut = self.di_container.bindings.borrow_mut(); + { + let bindings = self.di_container.bindings.borrow(); - if bindings_mut.has::<Interface>(None) { - return Err(BindingBuilderError::BindingAlreadyExists(type_name::< - Interface, - >())); + if bindings.has::<Interface>(None) { + return Err(BindingBuilderError::BindingAlreadyExists(type_name::< + Interface, + >( + ))); + } } + let mut bindings_mut = self.di_container.bindings.borrow_mut(); + let factory_impl = CastableFactory::new(factory_func); bindings_mut.set::<Interface>( None, Box::new(crate::provider::blocking::FactoryProvider::new( crate::ptr::FactoryPtr::new(factory_impl), + false, )), ); @@ -287,25 +296,34 @@ where #[cfg(feature = "factory")] pub fn to_default_factory<Return>( &self, - factory_func: &'static dyn Fn<(), Output = crate::ptr::TransientPtr<Return>>, + factory_func: &'static dyn Fn< + (Rc<DIContainer>,), + Output = crate::ptr::TransientPtr<Return>, + >, ) -> Result<BindingWhenConfigurator<Interface>, BindingBuilderError> where Return: 'static + ?Sized, { - let mut bindings_mut = self.di_container.bindings.borrow_mut(); + { + let bindings = self.di_container.bindings.borrow(); - if bindings_mut.has::<Interface>(None) { - return Err(BindingBuilderError::BindingAlreadyExists(type_name::< - Interface, - >())); + if bindings.has::<Interface>(None) { + return Err(BindingBuilderError::BindingAlreadyExists(type_name::< + Interface, + >( + ))); + } } + let mut bindings_mut = self.di_container.bindings.borrow_mut(); + let factory_impl = CastableFactory::new(factory_func); bindings_mut.set::<Interface>( None, Box::new(crate::provider::blocking::FactoryProvider::new( crate::ptr::FactoryPtr::new(factory_impl), + true, )), ); @@ -381,10 +399,11 @@ impl DIContainer let binding_providable = self.get_binding_providable::<Interface>(name, dependency_history)?; - Self::handle_binding_providable(binding_providable) + self.handle_binding_providable(binding_providable) } fn handle_binding_providable<Interface>( + self: &Rc<Self>, binding_providable: Providable, ) -> Result<SomePtr<Interface>, DIContainerError> where @@ -409,21 +428,29 @@ impl DIContainer )), #[cfg(feature = "factory")] Providable::Factory(factory_binding) => { - match factory_binding.clone().cast::<Interface>() { - Ok(factory) => Ok(SomePtr::Factory(factory)), - Err(_err) => { - use crate::interfaces::factory::IFactory; - - let default_factory = factory_binding - .cast::<dyn IFactory<(), Interface>>() - .map_err(|_| DIContainerError::CastFailed { - interface: type_name::<Interface>(), - binding_kind: "factory", - })?; - - Ok(SomePtr::Transient(default_factory())) - } - } + use crate::interfaces::factory::IFactory; + + let factory = factory_binding + .cast::<dyn IFactory<(Rc<DIContainer>,), Interface>>() + .map_err(|_| DIContainerError::CastFailed { + interface: type_name::<Interface>(), + binding_kind: "factory", + })?; + + Ok(SomePtr::Factory(factory(self.clone()).into())) + } + #[cfg(feature = "factory")] + Providable::DefaultFactory(factory_binding) => { + use crate::interfaces::factory::IFactory; + + let default_factory = factory_binding + .cast::<dyn IFactory<(Rc<DIContainer>,), Interface>>() + .map_err(|_| DIContainerError::CastFailed { + interface: type_name::<Interface>(), + binding_kind: "default factory", + })?; + + Ok(SomePtr::Transient(default_factory(self.clone()))) } } } @@ -690,12 +717,16 @@ mod tests assert_eq!(di_container.bindings.borrow().count(), 0); - di_container.bind::<IUserManagerFactory>().to_factory(&|| { - let user_manager: TransientPtr<dyn subjects::IUserManager> = - TransientPtr::new(subjects::UserManager::new()); + di_container + .bind::<IUserManagerFactory>() + .to_factory(&|_| { + Box::new(move || { + let user_manager: TransientPtr<dyn subjects::IUserManager> = + TransientPtr::new(subjects::UserManager::new()); - user_manager - })?; + user_manager + }) + })?; assert_eq!(di_container.bindings.borrow().count(), 1); @@ -715,11 +746,13 @@ mod tests di_container .bind::<IUserManagerFactory>() - .to_factory(&|| { - let user_manager: TransientPtr<dyn subjects::IUserManager> = - TransientPtr::new(subjects::UserManager::new()); + .to_factory(&|_| { + Box::new(move || { + let user_manager: TransientPtr<dyn subjects::IUserManager> = + TransientPtr::new(subjects::UserManager::new()); - user_manager + user_manager + }) })? .when_named("awesome")?; @@ -901,6 +934,8 @@ mod tests #[cfg(feature = "factory")] fn can_get_factory() -> Result<(), Box<dyn Error>> { + use crate::ptr::FactoryPtr; + trait IUserManager { fn add_user(&mut self, user_id: i128); @@ -940,8 +975,7 @@ mod tests use crate as syrette; #[crate::factory] - type IUserManagerFactory = - dyn crate::interfaces::factory::IFactory<(Vec<i128>,), dyn IUserManager>; + type IUserManagerFactory = dyn Fn(Vec<i128>) -> TransientPtr<dyn IUserManager>; mock! { Provider {} @@ -958,17 +992,26 @@ mod tests let di_container = DIContainer::new(); - let mut mock_provider = MockProvider::new(); + let factory_func: &'static dyn Fn< + (std::rc::Rc<DIContainer>,), + Output = Box< + dyn Fn<(Vec<i128>,), Output = crate::ptr::TransientPtr<dyn IUserManager>>, + >, + > = &|_: Rc<DIContainer>| { + Box::new(move |users| { + let user_manager: TransientPtr<dyn IUserManager> = + TransientPtr::new(UserManager::new(users)); - mock_provider.expect_provide().returning(|_, _| { - Ok(Providable::Factory(crate::ptr::FactoryPtr::new( - CastableFactory::new(&|users| { - let user_manager: TransientPtr<dyn IUserManager> = - TransientPtr::new(UserManager::new(users)); + user_manager + }) + }; - user_manager - }), - ))) + let mut mock_provider = MockProvider::new(); + + mock_provider.expect_provide().returning_st(|_, _| { + Ok(Providable::Factory(FactoryPtr::new(CastableFactory::new( + factory_func, + )))) }); di_container @@ -985,6 +1028,8 @@ mod tests #[cfg(feature = "factory")] fn can_get_factory_named() -> Result<(), Box<dyn Error>> { + use crate::ptr::FactoryPtr; + trait IUserManager { fn add_user(&mut self, user_id: i128); @@ -1024,8 +1069,7 @@ mod tests use crate as syrette; #[crate::factory] - type IUserManagerFactory = - dyn crate::interfaces::factory::IFactory<(Vec<i128>,), dyn IUserManager>; + type IUserManagerFactory = dyn Fn(Vec<i128>) -> TransientPtr<dyn IUserManager>; mock! { Provider {} @@ -1042,17 +1086,26 @@ mod tests let di_container = DIContainer::new(); - let mut mock_provider = MockProvider::new(); + let factory_func: &'static dyn Fn< + (std::rc::Rc<DIContainer>,), + Output = Box< + dyn Fn<(Vec<i128>,), Output = crate::ptr::TransientPtr<dyn IUserManager>>, + >, + > = &|_: Rc<DIContainer>| { + Box::new(move |users| { + let user_manager: TransientPtr<dyn IUserManager> = + TransientPtr::new(UserManager::new(users)); - mock_provider.expect_provide().returning(|_, _| { - Ok(Providable::Factory(crate::ptr::FactoryPtr::new( - CastableFactory::new(&|users| { - let user_manager: TransientPtr<dyn IUserManager> = - TransientPtr::new(UserManager::new(users)); + user_manager + }) + }; - user_manager - }), - ))) + let mut mock_provider = MockProvider::new(); + + mock_provider.expect_provide().returning_st(|_, _| { + Ok(Providable::Factory(FactoryPtr::new(CastableFactory::new( + factory_func, + )))) }); di_container diff --git a/src/provider/blocking.rs b/src/provider/blocking.rs index 69bbe78..e00786b 100644 --- a/src/provider/blocking.rs +++ b/src/provider/blocking.rs @@ -14,6 +14,10 @@ pub enum Providable Singleton(SingletonPtr<dyn Injectable>), #[cfg(feature = "factory")] Factory(crate::ptr::FactoryPtr<dyn crate::interfaces::any_factory::AnyFactory>), + #[cfg(feature = "factory")] + DefaultFactory( + crate::ptr::FactoryPtr<dyn crate::interfaces::any_factory::AnyFactory>, + ), } pub trait IProvider @@ -96,6 +100,7 @@ where pub struct FactoryProvider { factory: crate::ptr::FactoryPtr<dyn crate::interfaces::any_factory::AnyFactory>, + is_default_factory: bool, } #[cfg(feature = "factory")] @@ -103,9 +108,13 @@ impl FactoryProvider { pub fn new( factory: crate::ptr::FactoryPtr<dyn crate::interfaces::any_factory::AnyFactory>, + is_default_factory: bool, ) -> Self { - Self { factory } + Self { + factory, + is_default_factory, + } } } @@ -118,6 +127,10 @@ impl IProvider for FactoryProvider _dependency_history: Vec<&'static str>, ) -> Result<Providable, InjectableError> { - Ok(Providable::Factory(self.factory.clone())) + Ok(if self.is_default_factory { + Providable::DefaultFactory(self.factory.clone()) + } else { + Providable::Factory(self.factory.clone()) + }) } } |