aboutsummaryrefslogtreecommitdiff
path: root/macros/src
diff options
context:
space:
mode:
authorHampusM <hampus@hampusmat.com>2023-12-25 22:53:13 +0100
committerHampusM <hampus@hampusmat.com>2023-12-28 12:00:11 +0100
commit607d8d50b76665b67ca6f3124aa81773bec110b7 (patch)
tree142a7c17c4e254eabd5a5396975921bca1869bec /macros/src
parentc501a5cc770f632eba1529de09bd3ae2d7958de6 (diff)
WIP Linkme dependency removalwithout-linkme
Diffstat (limited to 'macros/src')
-rw-r--r--macros/src/caster.rs158
-rw-r--r--macros/src/declare_interface_args.rs177
-rw-r--r--macros/src/injectable/implementation.rs105
-rw-r--r--macros/src/injectable/macro_args.rs11
-rw-r--r--macros/src/lib.rs126
-rw-r--r--macros/src/util/syn_path.rs12
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(