use proc_macro2::{Span, TokenStream};
use quote::{format_ident, quote};
use syn::{Ident, LitStr};
use crate::event_parse::{EventEnumEntry, EventEnumInput, EventKind};
pub fn expand_event_type_enum(
input: EventEnumInput,
ruma_events: TokenStream,
) -> syn::Result<TokenStream> {
let ruma_serde = quote! { #ruma_events::exports::ruma_serde };
let mut room: Vec<&Vec<EventEnumEntry>> = vec![];
let mut state: Vec<&Vec<EventEnumEntry>> = vec![];
let mut message: Vec<&Vec<EventEnumEntry>> = vec![];
let mut ephemeral: Vec<&Vec<EventEnumEntry>> = vec![];
let mut room_account: Vec<&Vec<EventEnumEntry>> = vec![];
let mut global_account: Vec<&Vec<EventEnumEntry>> = vec![];
let mut to_device: Vec<&Vec<EventEnumEntry>> = vec![];
for event in &input.enums {
match event.name {
EventKind::GlobalAccountData => global_account.push(&event.events),
EventKind::RoomAccountData => room_account.push(&event.events),
EventKind::Ephemeral => ephemeral.push(&event.events),
EventKind::Message => {
message.push(&event.events);
room.push(&event.events);
}
EventKind::State => {
state.push(&event.events);
room.push(&event.events);
}
EventKind::ToDevice => to_device.push(&event.events),
EventKind::Redaction => {}
EventKind::Presence => {}
}
}
let presence = vec![EventEnumEntry {
attrs: vec![],
ev_type: LitStr::new("m.presence", Span::call_site()),
}];
let mut all = input.enums.iter().map(|e| &e.events).collect::<Vec<_>>();
all.push(&presence);
let (all_event_types, all_str_ev_types) = generate_variants(&all)?;
let all =
generate_enum(format_ident!("EventType"), all_str_ev_types, all_event_types, &ruma_serde);
let (room_event_types, room_str_ev_types) = generate_variants(&room)?;
let room = generate_enum(
format_ident!("RoomEventType"),
room_str_ev_types,
room_event_types,
&ruma_serde,
);
let (state_event_types, state_str_ev_types) = generate_variants(&state)?;
let state = generate_enum(
format_ident!("StateEventType"),
state_str_ev_types,
state_event_types,
&ruma_serde,
);
let (message_event_types, message_str_ev_types) = generate_variants(&message)?;
let message = generate_enum(
format_ident!("MessageEventType"),
message_str_ev_types,
message_event_types,
&ruma_serde,
);
let (ephemeral_event_types, ephemeral_str_ev_types) = generate_variants(&ephemeral)?;
let ephemeral = generate_enum(
format_ident!("EphemeralRoomEventType"),
ephemeral_str_ev_types,
ephemeral_event_types,
&ruma_serde,
);
let (room_account_event_types, room_account_str_ev_types) = generate_variants(&room_account)?;
let room_account = generate_enum(
format_ident!("RoomAccountDataEventType"),
room_account_str_ev_types,
room_account_event_types,
&ruma_serde,
);
let (global_account_event_types, global_account_str_ev_types) =
generate_variants(&global_account)?;
let global_account = generate_enum(
format_ident!("GlobalAccountDataEventType"),
global_account_str_ev_types,
global_account_event_types,
&ruma_serde,
);
let (to_device_event_types, to_device_str_ev_types) = generate_variants(&to_device)?;
let to_device = generate_enum(
format_ident!("ToDeviceEventType"),
to_device_str_ev_types,
to_device_event_types,
&ruma_serde,
);
Ok(quote! {
#all
#room
#state
#message
#ephemeral
#room_account
#global_account
#to_device
})
}
fn generate_enum(
ident: Ident,
ev_type_strings: Vec<&LitStr>,
variants: Vec<TokenStream>,
ruma_serde: &TokenStream,
) -> TokenStream {
let str_doc = format!("Creates a string slice from this `{}`.", ident);
let byte_doc = format!("Creates a byte slice from this `{}`.", ident);
let enum_doc = format!("The type of `{}` this is.", ident);
quote! {
#[doc = #enum_doc]
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, #ruma_serde::StringEnum)]
pub enum #ident {
#(
#[doc = #ev_type_strings]
#[ruma_enum(rename = #ev_type_strings)]
#variants,
)*
#[doc(hidden)]
_Custom(String),
}
impl #ident {
#[doc = #str_doc]
pub fn as_str(&self) -> &str {
self.as_ref()
}
#[doc = #byte_doc]
pub fn as_bytes(&self) -> &[u8] {
self.as_str().as_bytes()
}
}
}
}
fn generate_variants<'a>(
input: &'a [&Vec<EventEnumEntry>],
) -> syn::Result<(Vec<TokenStream>, Vec<&'a LitStr>)> {
let mut deduped: Vec<&EventEnumEntry> = vec![];
for item in input.iter().copied().flatten() {
if let Some(idx) = deduped.iter().position(|e| e.ev_type == item.ev_type) {
if deduped[idx].attrs != item.attrs && item.attrs.is_empty() {
deduped[idx] = item;
}
} else {
deduped.push(item);
}
}
let event_types = deduped.iter().map(|e| &e.ev_type).collect();
let event_types_variants =
deduped.iter().map(|e| Ok(e.to_variant()?.decl())).collect::<syn::Result<Vec<_>>>()?;
Ok((event_types_variants, event_types))
}