#![cfg_attr(doc_cfg, feature(doc_cfg))]
#![deny(clippy::all)]
#![deny(clippy::pedantic)]
#![allow(clippy::module_name_repetitions)]
#![deny(missing_docs)]
use proc_macro::TokenStream;
use quote::quote;
use syn::punctuated::Punctuated;
use syn::token::Dyn;
use syn::{
parse,
parse_macro_input,
TraitBound,
TraitBoundModifier,
Type,
TypeParamBound,
TypeTraitObject,
};
mod caster;
mod declare_interface_args;
mod injectable;
mod macro_flag;
mod util;
#[cfg(feature = "factory")]
#[cfg_attr(doc_cfg, doc(cfg(feature = "factory")))]
mod factory;
#[cfg(feature = "factory")]
#[cfg_attr(doc_cfg, doc(cfg(feature = "factory")))]
mod fn_trait;
#[cfg(test)]
mod test_utils;
use crate::caster::generate_caster;
use crate::declare_interface_args::DeclareInterfaceArgs;
use crate::injectable::dependency::Dependency;
use crate::injectable::implementation::InjectableImpl;
use crate::injectable::macro_args::InjectableMacroArgs;
#[cfg(not(tarpaulin_include))]
#[proc_macro_attribute]
pub fn injectable(args_stream: TokenStream, impl_stream: TokenStream) -> TokenStream
{
let InjectableMacroArgs { interface, flags } = parse_macro_input!(args_stream);
let no_doc_hidden = flags
.iter()
.find(|flag| flag.flag.to_string().as_str() == "no_doc_hidden")
.map_or(false, |flag| flag.is_on.value);
let no_declare_concrete_interface = flags
.iter()
.find(|flag| flag.flag.to_string().as_str() == "no_declare_concrete_interface")
.map_or(false, |flag| flag.is_on.value);
let is_async = flags
.iter()
.find(|flag| flag.flag.to_string().as_str() == "async")
.map_or(false, |flag| flag.is_on.value);
let injectable_impl: InjectableImpl<Dependency> = match parse(impl_stream) {
Ok(injectable_impl) => injectable_impl,
Err(err) => {
panic!("{err}");
}
};
let expanded_injectable_impl = injectable_impl.expand(no_doc_hidden, is_async);
let self_type = &injectable_impl.self_type;
let opt_interface = 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 async_flag = if is_async {
quote! {, async = true}
} else {
quote! {}
};
quote! {
syrette::declare_interface!(#self_type -> #interface #async_flag);
}
} else {
quote! {}
};
quote! {
#expanded_injectable_impl
#maybe_decl_interface
}
.into()
}
#[cfg(feature = "factory")]
#[cfg_attr(doc_cfg, doc(cfg(feature = "factory")))]
#[cfg(not(tarpaulin_include))]
#[proc_macro_attribute]
pub fn factory(args_stream: TokenStream, type_alias_stream: TokenStream) -> TokenStream
{
use quote::ToTokens;
use syn::parse_str;
use crate::factory::build_declare_interfaces::build_declare_factory_interfaces;
use crate::factory::macro_args::FactoryMacroArgs;
use crate::factory::type_alias::FactoryTypeAlias;
let FactoryMacroArgs { flags } = parse(args_stream).unwrap();
let mut is_threadsafe = flags
.iter()
.find(|flag| flag.flag.to_string().as_str() == "threadsafe")
.map_or(false, |flag| flag.is_on.value);
let is_async = flags
.iter()
.find(|flag| flag.flag.to_string().as_str() == "async")
.map_or(false, |flag| flag.is_on.value);
if is_async {
is_threadsafe = true;
}
let FactoryTypeAlias {
mut type_alias,
mut factory_interface,
arg_types: _,
return_type: _,
} = parse(type_alias_stream).unwrap();
let output = factory_interface.output.clone();
factory_interface.output = parse(
if is_async {
quote! {
syrette::future::BoxFuture<'static, syrette::ptr::TransientPtr<#output>>
}
} else {
quote! {
syrette::ptr::TransientPtr<#output>
}
}
.into(),
)
.unwrap();
if is_threadsafe {
factory_interface.add_trait_bound(parse_str("Send").unwrap());
factory_interface.add_trait_bound(parse_str("Sync").unwrap());
}
type_alias.ty = Box::new(Type::Verbatim(factory_interface.to_token_stream()));
let decl_interfaces =
build_declare_factory_interfaces(&factory_interface, is_threadsafe);
quote! {
#type_alias
#decl_interfaces
}
.into()
}
#[cfg(feature = "factory")]
#[cfg_attr(doc_cfg, doc(cfg(feature = "factory")))]
#[cfg(not(tarpaulin_include))]
#[proc_macro]
pub fn declare_default_factory(args_stream: TokenStream) -> TokenStream
{
use syn::parse_str;
use crate::factory::build_declare_interfaces::build_declare_factory_interfaces;
use crate::factory::declare_default_args::DeclareDefaultFactoryMacroArgs;
use crate::fn_trait::FnTrait;
let DeclareDefaultFactoryMacroArgs { interface, flags } = parse(args_stream).unwrap();
let mut is_threadsafe = flags
.iter()
.find(|flag| flag.flag.to_string().as_str() == "threadsafe")
.map_or(false, |flag| flag.is_on.value);
let is_async = flags
.iter()
.find(|flag| flag.flag.to_string().as_str() == "async")
.map_or(false, |flag| flag.is_on.value);
if is_async {
is_threadsafe = true;
}
let mut factory_interface: FnTrait = parse(
if is_async {
quote! {
dyn Fn() -> syrette::future::BoxFuture<
'static,
syrette::ptr::TransientPtr<#interface>
>
}
} else {
quote! {
dyn Fn() -> syrette::ptr::TransientPtr<#interface>
}
}
.into(),
)
.unwrap();
if is_threadsafe {
factory_interface.add_trait_bound(parse_str("Send").unwrap());
factory_interface.add_trait_bound(parse_str("Sync").unwrap());
}
build_declare_factory_interfaces(&factory_interface, is_threadsafe).into()
}
#[cfg(not(tarpaulin_include))]
#[proc_macro]
pub fn declare_interface(input: TokenStream) -> TokenStream
{
let DeclareInterfaceArgs {
implementation,
interface,
flags,
} = parse_macro_input!(input);
let opt_async_flag = flags
.iter()
.find(|flag| flag.flag.to_string().as_str() == "async");
let is_async =
opt_async_flag.map_or_else(|| false, |async_flag| async_flag.is_on.value);
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()
}
#[cfg(not(tarpaulin_include))]
#[proc_macro_attribute]
pub fn named(_: TokenStream, _: TokenStream) -> TokenStream
{
TokenStream::new()
}