use proc_macro::TokenStream;
use quote::quote;
use syn::parse::{Parse, ParseStream};
use syn::{Ident, Result as SynResult, Token, Type};
struct ChainStructInput {
type_name: Ident,
inner_type: Type,
}
impl Parse for ChainStructInput {
fn parse(input: ParseStream) -> SynResult<Self> {
let type_name = input.parse()?;
input.parse::<Token![=]>()?;
let inner_type = input.parse()?;
Ok(ChainStructInput {
type_name,
inner_type,
})
}
}
pub fn chain_struct(input: TokenStream) -> TokenStream {
let ChainStructInput {
type_name,
inner_type,
} = syn::parse_macro_input!(input as ChainStructInput);
#[cfg(not(feature = "serde"))]
let struct_def = quote! {
#[derive(Debug)]
pub struct #type_name {
inner: #inner_type,
}
};
#[cfg(feature = "serde")]
let struct_def = quote! {
#[derive(Debug, serde::Serialize)]
pub struct #type_name {
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 any_out_impl = quote! {
impl Into<mingling::AnyOutput> for #type_name {
fn into(self) -> mingling::AnyOutput {
mingling::AnyOutput::new(self)
}
}
impl Into<mingling::ChainProcess> for #type_name {
fn into(self) -> mingling::ChainProcess {
mingling::AnyOutput::new(self).route_chain()
}
}
impl #type_name {
pub fn to_chain(self) -> mingling::ChainProcess {
mingling::AnyOutput::new(self).route_chain()
}
pub fn to_render(self) -> mingling::ChainProcess {
mingling::AnyOutput::new(self).route_renderer()
}
}
};
let expanded = quote! {
#struct_def
#new_impl
#from_into_impl
#as_ref_impl
#deref_impl
#default_impl
#any_out_impl
};
expanded.into()
}