From c1e682c25c24be3174d44ceb95b0537c38299d0c Mon Sep 17 00:00:00 2001 From: HampusM Date: Mon, 12 Sep 2022 20:22:13 +0200 Subject: 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 --- macros/src/factory_type_alias.rs | 75 +++++++--------------------------------- macros/src/fn_trait.rs | 68 ++++++++++++++++++++++++++++++++++++ macros/src/lib.rs | 26 +++++++++----- 3 files changed, 98 insertions(+), 71 deletions(-) create mode 100644 macros/src/fn_trait.rs (limited to 'macros') 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, pub return_type: Type, } impl Parse for FactoryTypeAlias { - #[allow(clippy::match_wildcard_for_single_variants)] fn parse(input: ParseStream) -> syn::Result { let type_alias = match input.parse::() { @@ -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::(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, + pub r_arrow_token: Token![->], + pub output: Type, +} + +impl Parse for FnTrait +{ + fn parse(input: syn::parse::ParseStream) -> syn::Result + { + let dyn_token = input.parse::()?; + + let trait_ident = input.parse::()?; + + 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::]>()?; + + let output = input.parse::()?; + + 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,), dyn IConfigurator>; +/// type IConfiguratorFactory = dyn Fn(Vec) -> TransientPtr; /// ``` #[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,), + #factory_interface + > -> syrette::interfaces::factory::IFactory< + (std::rc::Rc,), + #factory_interface + > ); syrette::declare_interface!( syrette::castable_factory::blocking::CastableFactory< - #arg_types, - #return_type + (std::rc::Rc,), + #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,), #interface, - > -> syrette::interfaces::factory::IFactory<(), #interface> + > -> syrette::interfaces::factory::IFactory< + (std::rc::Rc,), + #interface + > ); syrette::declare_interface!( syrette::castable_factory::blocking::CastableFactory< - (), + (std::rc::Rc,), #interface, > -> syrette::interfaces::any_factory::AnyFactory ); -- cgit v1.2.3-18-g5258