use proc_macro::TokenStream;
use quote::quote;
use syn::parse::{Parse, ParseStream};
use syn::{Attribute, Ident, Result as SynResult, Token, Type};
enum PackInput {
Explicit {
attrs: Vec<Attribute>,
group_name: syn::Path,
type_name: Ident,
inner_type: Type,
},
Default {
attrs: Vec<Attribute>,
type_name: Ident,
inner_type: Type,
},
}
impl Parse for PackInput {
fn parse(input: ParseStream) -> SynResult<Self> {
let attrs = input.call(Attribute::parse_outer)?;
if (input.peek(Ident) || input.peek(Token![crate]))
&& (input.peek2(Token![,]) || input.peek2(Token![::]))
{
let group_name = input.parse::<syn::Path>()?;
input.parse::<Token![,]>()?;
let type_name = input.parse()?;
input.parse::<Token![=]>()?;
let inner_type = input.parse()?;
Ok(PackInput::Explicit {
attrs,
group_name,
type_name,
inner_type,
})
} else if input.peek(Ident) && input.peek2(Token![=]) {
let type_name = input.parse()?;
input.parse::<Token![=]>()?;
let inner_type = input.parse()?;
Ok(PackInput::Default {
attrs,
type_name,
inner_type,
})
} else {
Err(input.lookahead1().error())
}
}
}
pub fn pack(input: TokenStream) -> TokenStream {
let pack_input = syn::parse_macro_input!(input as PackInput);
let (group_name, type_name, inner_type, attrs, use_default) = match pack_input {
PackInput::Explicit {
attrs,
group_name,
type_name,
inner_type,
} => (quote! { #group_name }, type_name, inner_type, attrs, false),
PackInput::Default {
attrs,
type_name,
inner_type,
} => (
crate::default_program_path(),
type_name,
inner_type,
attrs,
true,
),
};
#[cfg(not(feature = "general_renderer"))]
let struct_def = quote! {
#(#attrs)*
pub struct #type_name {
pub(crate) inner: #inner_type,
}
};
#[cfg(feature = "general_renderer")]
let struct_def = quote! {
#(#attrs)*
#[derive(serde::Serialize)]
pub struct #type_name {
pub(crate) inner: #inner_type,
}
};
let new_impl = quote! {
impl #type_name {
pub fn new(inner: #inner_type) -> Self {
Self { inner }
}
}
};
let from_into_impl = quote! {
impl From<#inner_type> for #type_name {
fn from(inner: #inner_type) -> Self {
Self::new(inner)
}
}
impl From<#type_name> for #inner_type {
fn from(wrapper: #type_name) -> #inner_type {
wrapper.inner
}
}
};
let as_ref_impl = quote! {
impl ::std::convert::AsRef<#inner_type> for #type_name {
fn as_ref(&self) -> &#inner_type {
&self.inner
}
}
impl ::std::convert::AsMut<#inner_type> for #type_name {
fn as_mut(&mut self) -> &mut #inner_type {
&mut self.inner
}
}
};
let deref_impl = quote! {
impl ::std::ops::Deref for #type_name {
type Target = #inner_type;
fn deref(&self) -> &Self::Target {
&self.inner
}
}
impl ::std::ops::DerefMut for #type_name {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.inner
}
}
};
let default_impl = quote! {
impl ::std::default::Default for #type_name
where
#inner_type: ::std::default::Default,
{
fn default() -> Self {
Self::new(::std::default::Default::default())
}
}
};
let register_impl = quote! {
::mingling::macros::register_type!(#type_name);
};
let any_out_impl = quote! {
impl Into<mingling::AnyOutput<#group_name>> for #type_name {
fn into(self) -> mingling::AnyOutput<#group_name> {
mingling::AnyOutput::new(self)
}
}
impl Into<mingling::ChainProcess<#group_name>> for #type_name {
fn into(self) -> mingling::ChainProcess<#group_name> {
mingling::AnyOutput::new(self).route_chain()
}
}
impl #type_name {
pub fn to_chain(self) -> mingling::ChainProcess<#group_name> {
mingling::AnyOutput::new(self).route_chain()
}
pub fn to_render(self) -> mingling::ChainProcess<#group_name> {
mingling::AnyOutput::new(self).route_renderer()
}
}
};
let group_impl = quote! {
impl ::mingling::Groupped<#group_name> for #type_name {
fn member_id() -> #group_name {
#group_name::#type_name
}
}
};
let expanded = if use_default {
quote! {
#struct_def
#new_impl
#from_into_impl
#as_ref_impl
#deref_impl
#default_impl
#register_impl
impl Into<mingling::AnyOutput<#group_name>> for #type_name {
fn into(self) -> mingling::AnyOutput<#group_name> {
mingling::AnyOutput::new(self)
}
}
impl From<#type_name> for mingling::ChainProcess<#group_name> {
fn from(value: #type_name) -> Self {
mingling::AnyOutput::new(value).route_chain()
}
}
impl #type_name {
pub fn to_chain(self) -> mingling::ChainProcess<#group_name> {
mingling::AnyOutput::new(self).route_chain()
}
pub fn to_render(self) -> mingling::ChainProcess<#group_name> {
mingling::AnyOutput::new(self).route_renderer()
}
}
impl ::mingling::Groupped<#group_name> for #type_name {
fn member_id() -> #group_name {
#group_name::#type_name
}
}
}
} else {
quote! {
#struct_def
#new_impl
#from_into_impl
#as_ref_impl
#deref_impl
#default_impl
#register_impl
#any_out_impl
#group_impl
}
};
expanded.into()
}