use proc_macro2::TokenStream;
use quote::{format_ident, quote};
use syn::Path;
use super::naming::{event_type_to_handler_method_name, generate_unique_variant_names};
pub fn generate_event_subscriber(
subscriber_name: &syn::Ident,
event_types: &[Path],
variant_names: &[syn::Ident],
enum_name: &syn::Ident,
vis: &syn::Visibility,
subscribe_trait_path: TokenStream,
subscriber_method: TokenStream,
) -> TokenStream {
let num_events = event_types.len();
let sub_fields: Vec<_> = (0..num_events).map(|i| format_ident!("sub{}", i)).collect();
let field_defs: Vec<_> = event_types
.iter()
.zip(&sub_fields)
.map(|(event_type, field_name)| {
quote! {
#field_name: <#event_type as #subscribe_trait_path>::Subscriber
}
})
.collect();
let field_inits: Vec<_> = event_types
.iter()
.zip(&sub_fields)
.map(|(event_type, field_name)| {
quote! {
#field_name: <#event_type as #subscribe_trait_path>::#subscriber_method()
}
})
.collect();
let select_arms: Vec<_> = sub_fields
.iter()
.zip(variant_names)
.map(|(field_name, variant_name)| {
quote! {
event = self.#field_name.next_event().fuse() => #enum_name::#variant_name(event),
}
})
.collect();
quote! {
#vis struct #subscriber_name {
#(#field_defs),*
}
impl #subscriber_name {
pub fn new() -> Self {
Self {
#(#field_inits),*
}
}
}
impl ::rmk::event::EventSubscriber for #subscriber_name {
type Event = #enum_name;
async fn next_event(&mut self) -> Self::Event {
use ::rmk::event::EventSubscriber;
use ::rmk::futures::FutureExt;
::rmk::futures::select_biased! {
#(#select_arms)*
}
}
}
impl #subscribe_trait_path for #enum_name {
type Subscriber = #subscriber_name;
fn #subscriber_method() -> Self::Subscriber {
#subscriber_name::new()
}
}
}
}
pub fn generate_event_match_arms(
event_types: &[Path],
variant_names: &[syn::Ident],
enum_name: &syn::Ident,
) -> Vec<TokenStream> {
event_types
.iter()
.zip(variant_names)
.map(|(event_type, variant_name)| {
let method_name = event_type_to_handler_method_name(event_type);
quote! {
#enum_name::#variant_name(event) => self.#method_name(event).await
}
})
.collect()
}
pub fn generate_event_enum_and_dispatch(
struct_name: &syn::Ident,
vis: &syn::Visibility,
event_types: &[Path],
kind: &str,
subscribe_trait_path: TokenStream,
subscriber_method: TokenStream,
) -> (TokenStream, TokenStream, TokenStream, TokenStream) {
if event_types.len() == 1 {
let event_type = &event_types[0];
let method_name = event_type_to_handler_method_name(event_type);
(
quote! { #event_type },
quote! {},
quote! {},
quote! { self.#method_name(event).await },
)
} else {
let enum_name = format_ident!("{}{}EventEnum", struct_name, kind);
let subscriber_name = format_ident!("{}{}EventSubscriber", struct_name, kind);
let variant_names = generate_unique_variant_names(event_types);
let enum_variants: Vec<TokenStream> = event_types
.iter()
.zip(&variant_names)
.map(|(event_type, variant_name)| {
quote! { #variant_name(#event_type) }
})
.collect();
let match_arms = generate_event_match_arms(event_types, &variant_names, &enum_name);
let subscriber_impl = generate_event_subscriber(
&subscriber_name,
event_types,
&variant_names,
&enum_name,
vis,
subscribe_trait_path,
subscriber_method,
);
let enum_def = quote! {
#[derive(Clone)]
#vis enum #enum_name {
#(#enum_variants),*
}
};
(
quote! { #enum_name },
enum_def,
subscriber_impl,
quote! {
match event {
#(#match_arms),*
}
},
)
}
}