use proc_macro2::TokenStream;
use quote::{format_ident, quote};
use super::EventEnumKind;
use crate::{
events::common::{EventContentTraitVariation, EventVariation},
util::RumaEvents,
};
pub(super) fn expand_json_castable_impl(
ident: &syn::Ident,
kind: EventEnumKind,
variation: EventVariation,
ruma_events: &RumaEvents,
) -> TokenStream {
let ruma_common = ruma_events.ruma_common();
let mut json_castable_impls = quote! {
#[automatically_derived]
impl #ruma_common::serde::JsonCastable<#ruma_common::serde::JsonObject> for #ident {}
};
let mut event_kinds = vec![kind];
event_kinds.extend(kind.extra_enum_kinds());
for event_kind in event_kinds {
let event_variations = event_kind.event_variations();
json_castable_impls.extend(
event_variations
.iter()
.filter(|event_variation| event_variation.is_json_castable_to(variation))
.chain(event_variations.contains(&variation).then_some(&variation))
.map(|event_variation| {
let EventWithBounds { type_with_generics, impl_generics, where_clause } =
EventWithBounds::new(event_kind, *event_variation, ruma_events);
quote! {
#[automatically_derived]
impl #impl_generics #ruma_common::serde::JsonCastable<#ident> for #type_with_generics
#where_clause
{}
}
}),
);
let event_enum_variations = event_kind.event_enum_variations();
json_castable_impls.extend(
event_enum_variations
.iter()
.filter(|event_enum_variation| event_enum_variation.is_json_castable_to(variation))
.chain(
(event_kind != kind && event_enum_variations.contains(&variation))
.then_some(&variation),
)
.map(|event_enum_variation| {
let other_ident = event_kind.to_event_enum_ident(*event_enum_variation);
quote! {
#[automatically_derived]
impl #ruma_common::serde::JsonCastable<#ident> for #other_ident {}
}
}),
);
}
json_castable_impls
}
impl EventEnumKind {
pub(super) fn to_event_enum_ident(self, variation: EventVariation) -> syn::Ident {
format_ident!("Any{variation}{self}")
}
fn extra_enum_kinds(self) -> &'static [Self] {
match self {
Self::Timeline => &[Self::MessageLike, Self::State],
Self::GlobalAccountData
| Self::RoomAccountData
| Self::EphemeralRoom
| Self::MessageLike
| Self::State
| Self::ToDevice => &[],
}
}
}
struct EventWithBounds {
type_with_generics: TokenStream,
impl_generics: Option<TokenStream>,
where_clause: Option<TokenStream>,
}
impl EventWithBounds {
fn new(kind: EventEnumKind, variation: EventVariation, ruma_events: &RumaEvents) -> Self {
let ident = kind.to_event_ident(variation);
let event_content_trait = match variation {
EventVariation::None
| EventVariation::Sync
| EventVariation::Original
| EventVariation::OriginalSync
| EventVariation::Initial => {
if kind == EventEnumKind::State {
kind.to_content_kind_trait(EventContentTraitVariation::Static)
} else {
kind.to_content_kind_trait(EventContentTraitVariation::Original)
}
}
EventVariation::Stripped => {
kind.to_content_kind_trait(EventContentTraitVariation::PossiblyRedacted)
}
EventVariation::Redacted | EventVariation::RedactedSync => {
kind.to_content_kind_trait(EventContentTraitVariation::Redacted)
}
};
let (type_with_generics, impl_generics, where_clause) = match kind {
EventEnumKind::MessageLike | EventEnumKind::State
if matches!(variation, EventVariation::None | EventVariation::Sync) =>
{
let redacted_trait =
kind.to_content_kind_trait(EventContentTraitVariation::Redacted);
(
quote! { #ruma_events::#ident<C> },
Some(
quote! { <C: #ruma_events::#event_content_trait + #ruma_events::RedactContent> },
),
Some(quote! {
where
C::Redacted: #ruma_events::#redacted_trait,
}),
)
}
EventEnumKind::GlobalAccountData
| EventEnumKind::RoomAccountData
| EventEnumKind::EphemeralRoom
| EventEnumKind::MessageLike
| EventEnumKind::State
| EventEnumKind::ToDevice => (
quote! { #ruma_events::#ident<C> },
Some(quote! { <C: #ruma_events::#event_content_trait> }),
None,
),
EventEnumKind::Timeline => {
panic!("Timeline kind should not generate JsonCastable implementations")
}
};
Self { impl_generics, type_with_generics, where_clause }
}
}