diff options
Diffstat (limited to 'macros/src')
-rw-r--r-- | macros/src/caster.rs | 158 | ||||
-rw-r--r-- | macros/src/declare_interface_args.rs | 177 | ||||
-rw-r--r-- | macros/src/injectable/implementation.rs | 105 | ||||
-rw-r--r-- | macros/src/injectable/macro_args.rs | 11 | ||||
-rw-r--r-- | macros/src/lib.rs | 126 | ||||
-rw-r--r-- | macros/src/util/syn_path.rs | 12 |
6 files changed, 115 insertions, 474 deletions
diff --git a/macros/src/caster.rs b/macros/src/caster.rs deleted file mode 100644 index 417a881..0000000 --- a/macros/src/caster.rs +++ /dev/null @@ -1,158 +0,0 @@ -/** - * Originally from Intertrait by CodeChain - * - * <https://github.com/CodeChain-io/intertrait> - * <https://crates.io/crates/intertrait/0.2.2> - * - * Licensed under either of - * - * Apache License, Version 2.0 (LICENSE-APACHE or <http://www.apache.org/licenses/LICENSE-2.0>) - * MIT license (LICENSE-MIT or <http://opensource.org/licenses/MIT>) - - * at your option. -*/ -use std::str::from_utf8; - -use proc_macro2::{Ident, TokenStream}; -use quote::{format_ident, quote, ToTokens}; -use uuid::adapter::Simple; -use uuid::Uuid; - -#[cfg(syrette_macros_logging)] -use crate::util::tokens::ToTokensExt; - -const CASTER_FN_NAME_PREFIX: &[u8] = b"__"; - -const FN_BUF_LEN: usize = CASTER_FN_NAME_PREFIX.len() + Simple::LENGTH; - -#[cfg_attr(syrette_macros_logging, tracing::instrument(skip(ty, dst_trait)))] -pub fn generate_caster( - ty: &impl ToTokens, - dst_trait: &impl ToTokens, - sync: bool, -) -> TokenStream -{ - #[cfg(syrette_macros_logging)] - tracing::debug!( - source = %ty.to_str_pretty(), - destination = %ty.to_str_pretty(), - "Generating caster", - ); - - let fn_ident = create_caster_fn_ident(Uuid::new_v4()); - - let new_caster = if sync { - quote! { - syrette::private::cast::Caster::<#dst_trait>::new_sync( - |from| { - let concrete = from - .downcast::<#ty>() - .map_err(|_| syrette::private::cast::CasterError::CastBoxFailed)?; - - Ok(concrete as Box<#dst_trait>) - }, - |from| { - let concrete = from - .downcast::<#ty>() - .map_err(|_| syrette::private::cast::CasterError::CastRcFailed)?; - - Ok(concrete as std::rc::Rc<#dst_trait>) - }, - |from| { - let concrete = from - .downcast::<#ty>() - .map_err(|_| syrette::private::cast::CasterError::CastArcFailed)?; - - Ok(concrete as std::sync::Arc<#dst_trait>) - }, - ) - } - } else { - quote! { - syrette::private::cast::Caster::<#dst_trait>::new( - |from| { - let concrete = from - .downcast::<#ty>() - .map_err(|_| syrette::private::cast::CasterError::CastBoxFailed)?; - - Ok(concrete as Box<#dst_trait>) - }, - |from| { - let concrete = from - .downcast::<#ty>() - .map_err(|_| syrette::private::cast::CasterError::CastRcFailed)?; - - Ok(concrete as std::rc::Rc<#dst_trait>) - }, - ) - } - }; - - quote! { - #[syrette::private::linkme::distributed_slice(syrette::private::cast::CASTERS)] - #[linkme(crate = syrette::private::linkme)] - fn #fn_ident() -> (::std::any::TypeId, syrette::private::cast::BoxedCaster) { - (::std::any::TypeId::of::<#ty>(), Box::new(#new_caster)) - } - } -} - -fn create_caster_fn_ident(uuid: impl IUuid) -> Ident -{ - let buf = &mut [0u8; FN_BUF_LEN]; - - buf[..CASTER_FN_NAME_PREFIX.len()].copy_from_slice(CASTER_FN_NAME_PREFIX); - - uuid.encode_simple_lower_into(&mut buf[CASTER_FN_NAME_PREFIX.len()..]); - - let fn_name = - from_utf8(&buf[..FN_BUF_LEN]).expect("Created caster function name is not UTF-8"); - - format_ident!("{}", fn_name) -} - -/// Simple interface for `Uuid`. -/// -/// Created for ease of testing the [`create_caster_fn_ident`] function. -/// -/// [`Uuid`]: uuid::Uuid -#[cfg_attr(test, mockall::automock)] -trait IUuid -{ - /// Writes the Uuid as a simple lower-case string to `buf`. - fn encode_simple_lower_into(self, buf: &mut [u8]); -} - -impl IUuid for Uuid -{ - fn encode_simple_lower_into(self, buf: &mut [u8]) - { - self.to_simple().encode_lower(buf); - } -} - -#[cfg(test)] -mod tests -{ - use pretty_assertions::assert_eq; - - use super::*; - - #[test] - fn can_create_caster_fn_ident() - { - let mut uuid_mock = MockIUuid::new(); - - uuid_mock - .expect_encode_simple_lower_into() - .return_once(|buf| { - buf[..FN_BUF_LEN - 2].fill(b'f'); - }) - .once(); - - assert_eq!( - create_caster_fn_ident(uuid_mock), - format_ident!("__{}", "f".repeat(FN_BUF_LEN - 2)) - ); - } -} diff --git a/macros/src/declare_interface_args.rs b/macros/src/declare_interface_args.rs deleted file mode 100644 index 6556153..0000000 --- a/macros/src/declare_interface_args.rs +++ /dev/null @@ -1,177 +0,0 @@ -use syn::parse::{Parse, ParseStream}; -use syn::punctuated::Punctuated; -use syn::{Token, TypePath}; - -use crate::macro_flag::MacroFlag; -use crate::util::iterator_ext::IteratorExt; - -pub const DECLARE_INTERFACE_FLAGS: &[&str] = &["threadsafe_sharable"]; - -pub struct DeclareInterfaceArgs -{ - pub implementation: TypePath, - pub interface: TypePath, - pub flags: Punctuated<MacroFlag, Token![,]>, -} - -impl Parse for DeclareInterfaceArgs -{ - fn parse(input: ParseStream) -> syn::Result<Self> - { - let implementation: TypePath = input.parse()?; - - input.parse::<Token![->]>()?; - - let interface: TypePath = input.parse()?; - - let flags = if input.peek(Token![,]) { - input.parse::<Token![,]>()?; - - let flags = Punctuated::<MacroFlag, Token![,]>::parse_terminated(input)?; - - for flag in &flags { - let flag_name = flag.name().to_string(); - - if !DECLARE_INTERFACE_FLAGS.contains(&flag_name.as_str()) { - return Err(input.error(format!( - "Unknown flag '{flag_name}'. Expected one of [ {} ]", - DECLARE_INTERFACE_FLAGS.join(",") - ))); - } - } - - if let Some((dupe_flag, _)) = flags.iter().find_duplicate() { - return Err(input.error(format!("Duplicate flag '{}'", dupe_flag.name()))); - } - - flags - } else { - Punctuated::new() - }; - - Ok(Self { - implementation, - interface, - flags, - }) - } -} - -#[cfg(test)] -mod tests -{ - use proc_macro2::Span; - use quote::{format_ident, quote}; - use syn::{parse2, Lit, LitBool}; - - use super::*; - use crate::macro_flag::MacroFlagValue; - use crate::test_utils; - - #[test] - fn can_parse_with_no_flags() - { - let input_args = quote! { - Foo -> IFoo - }; - - let decl_interface_args = parse2::<DeclareInterfaceArgs>(input_args).unwrap(); - - assert_eq!( - decl_interface_args.implementation, - TypePath { - qself: None, - path: test_utils::create_path(&[test_utils::create_path_segment( - format_ident!("Foo"), - &[] - )]) - } - ); - - assert_eq!( - decl_interface_args.interface, - TypePath { - qself: None, - path: test_utils::create_path(&[test_utils::create_path_segment( - format_ident!("IFoo"), - &[] - )]) - } - ); - - assert!(decl_interface_args.flags.is_empty()); - } - - #[test] - fn can_parse_with_flags() - { - let input_args = quote! { - Foobar -> IFoobar, threadsafe_sharable = true - }; - - let decl_interface_args = parse2::<DeclareInterfaceArgs>(input_args).unwrap(); - - assert_eq!( - decl_interface_args.implementation, - TypePath { - qself: None, - path: test_utils::create_path(&[test_utils::create_path_segment( - format_ident!("Foobar"), - &[] - )]) - } - ); - - assert_eq!( - decl_interface_args.interface, - TypePath { - qself: None, - path: test_utils::create_path(&[test_utils::create_path_segment( - format_ident!("IFoobar"), - &[] - )]) - } - ); - - assert_eq!( - decl_interface_args.flags, - Punctuated::from_iter(vec![MacroFlag { - name: format_ident!("threadsafe_sharable"), - value: MacroFlagValue::Literal(Lit::Bool(LitBool::new( - true, - Span::call_site() - ))) - }]) - ); - } - - #[test] - fn cannot_parse_with_invalid_flag() - { - let input_args = quote! { - Foobar -> IFoobar, xyz = false, threadsafe_sharable = true - }; - - assert!(parse2::<DeclareInterfaceArgs>(input_args).is_err()); - } - - #[test] - fn cannot_parse_with_duplicate_flag() - { - assert!( - // Formatting is weird without this comment - parse2::<DeclareInterfaceArgs>(quote! { - Foobar -> IFoobar, threadsafe_sharable = true, threadsafe_sharable = true - }) - .is_err() - ); - - assert!( - // Formatting is weird without this comment - parse2::<DeclareInterfaceArgs>(quote! { - Foobar -> IFoobar, threadsafe_sharable = true, threadsafe_sharable = false - }) - .is_err() - ); - } -} diff --git a/macros/src/injectable/implementation.rs b/macros/src/injectable/implementation.rs index a67b4a3..a361567 100644 --- a/macros/src/injectable/implementation.rs +++ b/macros/src/injectable/implementation.rs @@ -1,4 +1,4 @@ -use proc_macro2::{Ident, Span}; +use proc_macro2::{Ident, Span, TokenStream}; use quote::{format_ident, quote, ToTokens}; use syn::spanned::Spanned; use syn::{ @@ -12,8 +12,10 @@ use syn::{ ImplItemMethod, ItemImpl, MethodTurbofish, + Path, ReturnType, Type, + TypePath, }; use crate::injectable::dependency::DependencyError; @@ -148,14 +150,13 @@ impl InjectableImpl Ok(()) } - pub fn self_type(&self) -> &Type - { - &self.original_impl.self_ty - } - #[cfg(not(tarpaulin_include))] - pub fn expand(&self, no_doc_hidden: bool, is_async: bool) - -> proc_macro2::TokenStream + pub fn expand( + &self, + no_doc_hidden: bool, + is_async: bool, + interface_trait: Option<&TypePath>, + ) -> proc_macro2::TokenStream { let di_container_var = format_ident!("{}", DI_CONTAINER_VAR_NAME); let dependency_history_var = format_ident!("{}", DEPENDENCY_HISTORY_VAR_NAME); @@ -186,6 +187,7 @@ impl InjectableImpl &dependency_history_var, &maybe_prevent_circular_deps, &get_dep_method_calls, + interface_trait, ) } else { self.expand_blocking_impl( @@ -194,6 +196,7 @@ impl InjectableImpl &dependency_history_var, &maybe_prevent_circular_deps, &get_dep_method_calls, + interface_trait, ) }; @@ -232,12 +235,31 @@ impl InjectableImpl dependency_history_var: &Ident, maybe_prevent_circular_deps: &proc_macro2::TokenStream, get_dep_method_calls: &Vec<proc_macro2::TokenStream>, + interface_trait: Option<&TypePath>, ) -> proc_macro2::TokenStream { let generics = &self.original_impl.generics; let self_type = &self.original_impl.self_ty; let constructor = &self.constructor_method.sig.ident; + let into_ptr_buf_fns = &[ + Self::create_to_ptr_buf_fn( + interface_trait, + &format_ident!("Box"), + Path::new_empty(), + ), + Self::create_to_ptr_buf_fn( + interface_trait, + &format_ident!("Rc"), + syn_path!(std::rc), + ), + Self::create_to_ptr_buf_fn( + interface_trait, + &format_ident!("Arc"), + syn_path!(std::sync), + ), + ] as &[TokenStream]; + let dependency_idents = (0..get_dep_method_calls.len()) .map(|index| format_ident!("dependency_{index}")) .collect::<Vec<_>>(); @@ -289,6 +311,8 @@ impl InjectableImpl )#maybe_await_constructor)) }) } + + #(#into_ptr_buf_fns)* } } } @@ -301,12 +325,31 @@ impl InjectableImpl dependency_history_var: &Ident, maybe_prevent_circular_deps: &proc_macro2::TokenStream, get_dep_method_calls: &Vec<proc_macro2::TokenStream>, + interface_trait: Option<&TypePath>, ) -> proc_macro2::TokenStream { let generics = &self.original_impl.generics; let self_type = &self.original_impl.self_ty; let constructor = &self.constructor_method.sig.ident; + let into_ptr_buf_fns = &[ + Self::create_to_ptr_buf_fn( + interface_trait, + &format_ident!("Box"), + Path::new_empty(), + ), + Self::create_to_ptr_buf_fn( + interface_trait, + &format_ident!("Rc"), + syn_path!(std::rc), + ), + Self::create_to_ptr_buf_fn( + interface_trait, + &format_ident!("Arc"), + syn_path!(std::sync), + ), + ] as &[TokenStream]; + quote! { #maybe_doc_hidden impl #generics syrette::interfaces::injectable::Injectable< @@ -332,6 +375,52 @@ impl InjectableImpl #(#get_dep_method_calls),* ))); } + + #(#into_ptr_buf_fns)* + } + } + } + + fn create_to_ptr_buf_fn( + interface_trait: Option<&TypePath>, + smart_ptr_ident: &Ident, + smart_ptr_parent_path: Path, + ) -> TokenStream + { + let ptr_buf_path = quote! { syrette::ptr_buffer::PtrBuffer }; + + let mut smart_ptr = smart_ptr_parent_path; + + smart_ptr.segments.push(smart_ptr_ident.clone().into()); + + let body = match interface_trait { + Some(interface_trait) => { + quote! { + let me: #smart_ptr<dyn #interface_trait> = self; + + #ptr_buf_path::new_from( + syrette::ptr_buffer::SmartPtr::#smart_ptr_ident(me) + ) + } + } + None => { + quote! { + #ptr_buf_path::new_from( + syrette::ptr_buffer::SmartPtr::#smart_ptr_ident(self) + ) + } + } + }; + + let into_ptr_buf_fn = format_ident!( + "into_ptr_buffer_{}", + smart_ptr_ident.to_string().to_lowercase() + ); + + quote! { + fn #into_ptr_buf_fn(self: #smart_ptr<Self>) -> #ptr_buf_path + { + #body } } } diff --git a/macros/src/injectable/macro_args.rs b/macros/src/injectable/macro_args.rs index 719d551..f0469f7 100644 --- a/macros/src/injectable/macro_args.rs +++ b/macros/src/injectable/macro_args.rs @@ -7,12 +7,7 @@ use crate::macro_flag::MacroFlag; use crate::util::error::diagnostic_error_enum; use crate::util::iterator_ext::IteratorExt; -pub const INJECTABLE_MACRO_FLAGS: &[&str] = &[ - "no_doc_hidden", - "async", - "no_declare_concrete_interface", - "constructor", -]; +pub const INJECTABLE_MACRO_FLAGS: &[&str] = &["no_doc_hidden", "async", "constructor"]; pub struct InjectableMacroArgs { @@ -194,7 +189,7 @@ mod tests fn can_parse_with_flags_only() { let input_args = quote! { - async = false, no_declare_concrete_interface = false + async = false, no_doc_hidden = false }; let injectable_macro_args = parse2::<InjectableMacroArgs>(input_args).unwrap(); @@ -212,7 +207,7 @@ mod tests ))) }, MacroFlag { - name: format_ident!("no_declare_concrete_interface"), + name: format_ident!("no_doc_hidden"), value: MacroFlagValue::Literal(Lit::Bool(LitBool::new( false, Span::call_site() diff --git a/macros/src/lib.rs b/macros/src/lib.rs index b9b6ea5..3d60de7 100644 --- a/macros/src/lib.rs +++ b/macros/src/lib.rs @@ -9,27 +9,13 @@ use proc_macro::TokenStream; use proc_macro_error::{proc_macro_error, set_dummy, ResultExt}; use quote::{format_ident, quote}; -use syn::punctuated::Punctuated; -use syn::token::Dyn; -use syn::{ - parse, - ItemImpl, - TraitBound, - TraitBoundModifier, - Type, - TypeParamBound, - TypeTraitObject, -}; +use syn::{parse, ItemImpl}; -use crate::caster::generate_caster; -use crate::declare_interface_args::DeclareInterfaceArgs; use crate::injectable::dummy::expand_dummy_blocking_impl; use crate::injectable::implementation::{InjectableImpl, InjectableImplError}; use crate::injectable::macro_args::InjectableMacroArgs; use crate::macro_flag::MacroFlag; -mod caster; -mod declare_interface_args; mod injectable; mod macro_flag; mod util; @@ -57,12 +43,6 @@ const PACKAGE_VERSION: &str = env!("CARGO_PKG_VERSION"); /// **Default:** `false`<br> /// Don't hide the impl of the [`Injectable`] trait from documentation. /// -/// #### `no_declare_concrete_interface` -/// **Value:** boolean literal<br> -/// **Default:** `false`<br> -/// Disable declaring the concrete type as the interface when no interface trait argument -/// is given. -/// /// #### `async` /// <span class="cf">Available on <strong>crate feature <code>async</code></strong> only. /// </span> @@ -79,10 +59,7 @@ const PACKAGE_VERSION: &str = env!("CARGO_PKG_VERSION"); /// Constructor method name. /// /// # Important -/// When no interface trait argument is given, you have three options -/// - Manually declare the interface with the [`declare_interface!`] macro. -/// - Use the [`di_container_bind`] macro to create a DI container binding. -/// - Use the concrete type as the interface. +/// When no interface trait argument is given, the concrete type is used as a interface. /// /// # Examples /// ``` @@ -216,13 +193,6 @@ pub fn injectable(args_stream: TokenStream, input_stream: TokenStream) -> TokenS .map_or(Ok(false), MacroFlag::get_bool) .unwrap_or_abort(); - let no_declare_concrete_interface = args - .flags - .iter() - .find(|flag| flag.name() == "no_declare_concrete_interface") - .map_or(Ok(false), MacroFlag::get_bool) - .unwrap_or_abort(); - let constructor = args .flags .iter() @@ -258,103 +228,15 @@ pub fn injectable(args_stream: TokenStream, input_stream: TokenStream) -> TokenS injectable_impl.validate(is_async).unwrap_or_abort(); - let expanded_injectable_impl = injectable_impl.expand(no_doc_hidden, is_async); - - let self_type = injectable_impl.self_type(); - - let opt_interface = args.interface.map(Type::Path).or_else(|| { - if no_declare_concrete_interface { - None - } else { - Some(self_type.clone()) - } - }); - - let maybe_decl_interface = if let Some(interface) = opt_interface { - let threadsafe_sharable_flag = if is_async { - quote! { , threadsafe_sharable = true } - } else { - quote! {} - }; - - quote! { - syrette::declare_interface!( - #self_type -> #interface #threadsafe_sharable_flag - ); - } - } else { - quote! {} - }; + let expanded_injectable_impl = + injectable_impl.expand(no_doc_hidden, is_async, args.interface.as_ref()); quote! { #expanded_injectable_impl - - #maybe_decl_interface } .into() } -/// Declares the interface trait of a implementation. -/// -/// # Arguments -/// {Implementation} -> {Interface} -/// * (Zero or more) Flags. Like `a = true, b = false` -/// -/// # Flags -/// - `threadsafe_sharable` - Enables the use of thread-safe shared instances of the -/// implementation accessed with the interface. -/// -/// # Examples -/// ``` -/// # use syrette::declare_interface; -/// # -/// # trait INinja {} -/// # -/// # struct Ninja {} -/// # -/// # impl INinja for Ninja {} -/// # -/// declare_interface!(Ninja -> INinja); -/// ``` -#[cfg(not(tarpaulin_include))] -#[proc_macro_error] -#[proc_macro] -pub fn declare_interface(input: TokenStream) -> TokenStream -{ - let DeclareInterfaceArgs { - implementation, - interface, - flags, - } = parse(input).unwrap_or_abort(); - - let threadsafe_sharable_flag = flags - .iter() - .find(|flag| flag.name() == "threadsafe_sharable"); - - let is_async = threadsafe_sharable_flag - .map_or_else(|| Ok(false), MacroFlag::get_bool) - .unwrap_or_abort(); - - #[cfg(syrette_macros_logging)] - init_logging(); - - let interface_type = if interface == implementation { - Type::Path(interface) - } else { - Type::TraitObject(TypeTraitObject { - dyn_token: Some(Dyn::default()), - bounds: Punctuated::from_iter(vec![TypeParamBound::Trait(TraitBound { - paren_token: None, - modifier: TraitBoundModifier::None, - lifetimes: None, - path: interface.path, - })]), - }) - }; - - generate_caster(&implementation, &interface_type, is_async).into() -} - /// Used to declare the name of a dependency in the constructor of a impl block decorated /// with [`macro@injectable`]. /// diff --git a/macros/src/util/syn_path.rs b/macros/src/util/syn_path.rs index 26e2597..6efea01 100644 --- a/macros/src/util/syn_path.rs +++ b/macros/src/util/syn_path.rs @@ -1,16 +1,26 @@ use std::fmt::Write; use quote::ToTokens; -use syn::punctuated::Pair; +use syn::punctuated::{Pair, Punctuated}; pub trait SynPathExt { + fn new_empty() -> Self; + /// Converts the [`syn::Path`] to a [`String`]. fn to_string(&self) -> String; } impl SynPathExt for syn::Path { + fn new_empty() -> Self + { + Self { + leading_colon: None, + segments: Punctuated::new(), + } + } + fn to_string(&self) -> String { self.segments.pairs().map(Pair::into_tuple).fold( |