use proc_macro2::TokenStream;
use syn::{DataEnum, Variant, Visibility, spanned::Spanned};
use proc_macro2::Ident;
use quote::{ToTokens, quote};
use crate::basic::{EnumDefinition, do_expand, extract_enum};
fn generate_normal_function(
basic: &Ident,
variant: &Variant,
block: bool,
no_async: bool,
vis: &Visibility,
) -> syn::Result<TokenStream> {
let mut ret = TokenStream::new();
let definition = EnumDefinition::try_from(variant)?;
let arg_def = definition.fields().to_arg_def(variant.span());
let arg = definition.fields().to_arg(variant.span());
let function_name = definition.get_name(variant.span());
let member = &variant.ident;
if !no_async {
let result = quote! {
#vis async fn #function_name (&self, #arg_def) -> std::option::Option<()> {
self.sender
.send(#basic::#member #arg)
.await
.ok()
}
};
ret.extend(result);
}
if block {
let function_name = if no_async {
function_name
} else {
definition.get_name_block(variant.span())
};
let result = quote! {
#vis fn #function_name (&self, #arg_def) -> std::option::Option<()> {
self.sender
.blocking_send(#basic::#member #arg)
.ok()
}
};
ret.extend(result);
}
Ok(ret)
}
fn generate_waitable_function(
basic: &Ident,
variant: &Variant,
return_type: TokenStream,
block: bool,
no_async: bool,
vis: &Visibility,
) -> syn::Result<TokenStream> {
let mut ret = TokenStream::new();
let definition = EnumDefinition::try_from(variant)?;
let arg_def = definition.fields().to_arg_def(variant.span());
let arg = definition.fields().enchant_arg(variant.span());
let function_name = definition.get_name(variant.span());
let member = &variant.ident;
if !no_async {
let q = quote! {
#vis async fn #function_name (&self, #arg_def) -> std::option::Option< #return_type > {
let (__private_sender, __private_r) = tokio::sync::oneshot::channel();
self.sender
.send(#basic::#member #arg)
.await
.ok();
__private_r.await.ok()
}
};
ret.extend(q);
};
if block {
let function_name = if no_async {
function_name
} else {
definition.get_name_block(variant.span())
};
let q = quote! {
#vis fn #function_name (&self, #arg_def) -> std::option::Option< #return_type > {
let (__private_sender, __private_r) = tokio::sync::oneshot::channel();
self.sender
.blocking_send(#basic::#member #arg)
.ok();
__private_r.blocking_recv().ok()
}
};
ret.extend(q);
}
Ok(ret)
}
fn generate_function(
st: &syn::DeriveInput,
de: &DataEnum,
block: bool,
no_async: bool,
vis: &Visibility,
) -> syn::Result<TokenStream> {
let mut ret = TokenStream::new();
let basic = &st.ident;
for variant in &de.variants {
let return_type = parse_return_type(variant);
let token = if let Some(ret) = return_type {
generate_waitable_function(basic, variant, ret, block, no_async, vis)
} else {
generate_normal_function(basic, variant, block, no_async, vis)
}?;
ret.extend(token);
}
Ok(ret)
}
fn parse_return_type(variant: &Variant) -> Option<TokenStream> {
for attr in &variant.attrs {
match &attr.meta {
syn::Meta::List(list) => {
if !list.path.segments.first()?.ident.eq("ret") {
continue;
}
return Some(list.tokens.clone());
}
_ => continue,
}
}
None
}
fn generate_member(_st: &syn::DeriveInput, de: &DataEnum) -> syn::Result<TokenStream> {
let mut ret = TokenStream::new();
for variant in &de.variants {
let return_type = parse_return_type(variant);
let definition = EnumDefinition::try_from(variant)?;
let arg_def = definition.fields().enum_arg_def(return_type);
let enum_name = definition.get_normal_name(variant.span());
let result = quote! {
#enum_name #arg_def ,
};
ret.extend(result);
}
Ok(ret)
}
fn do_first_expand(st: &syn::DeriveInput) -> syn::Result<TokenStream> {
let data_enum = extract_enum(st)?;
let enum_ident = &st.ident;
let mut attrs = TokenStream::new();
st.attrs.iter().for_each(|x| {
if let syn::Attribute {
meta:
syn::Meta::List(syn::MetaList {
path: syn::Path { segments, .. },
..
}),
..
} = x
&& segments.first().is_some_and(|x| x.ident.eq("helper"))
{
return;
}
x.to_tokens(&mut attrs)
});
let member_function = generate_member(st, data_enum)?;
let ret = quote! {
#attrs
pub enum #enum_ident {
#member_function
}
};
Ok(ret)
}
pub(crate) fn handle_new(input: syn::DeriveInput) -> syn::Result<TokenStream> {
crate::basic::early_check(&input)?;
do_first_expand(&input).and_then(|mut stream| {
stream.extend(do_expand(&input, Some(generate_function))?);
Ok(stream)
})
}