use std::{collections::BTreeMap, ops::Deref};
use proc_macro2::{Span, TokenStream};
use quote::{format_ident, quote};
use crate::{
events::{
common::{EventContentTraitVariation, EventVariation},
event_enum::{EventContentVariation, EventEnum, EventEnumKind},
},
util::RumaEventsReexport,
};
pub(super) struct EventContentEnums<'a> {
inner: &'a EventEnum<'a>,
enums: BTreeMap<EventContentVariation, EventContentEnumVariation<'a>>,
change: Option<EventContentChangeEnum<'a>>,
}
impl<'a> EventContentEnums<'a> {
pub(super) fn new(inner: &'a EventEnum<'a>) -> Self {
Self { inner, enums: BTreeMap::new(), change: None }
}
pub(super) fn get_or_create(
&mut self,
event_variation: EventVariation,
) -> Option<&EventContentEnumVariation<'a>> {
let variation = EventContentVariation::from_event_variation(event_variation)?;
Some(
self.enums
.entry(variation)
.or_insert_with(|| EventContentEnumVariation::new(self.inner, variation)),
)
}
pub(super) fn event_content_change_enum(&mut self) -> &EventContentChangeEnum<'a> {
self.change.get_or_insert_with(|| EventContentChangeEnum::new(self.inner))
}
pub(super) fn expand(&self) -> TokenStream {
self.enums
.values()
.map(EventContentEnumVariation::expand)
.chain(self.change.iter().map(EventContentChangeEnum::expand))
.collect()
}
}
pub(super) struct EventContentEnumVariation<'a> {
inner: &'a EventEnum<'a>,
variation: EventContentVariation,
ident: syn::Ident,
event_content_types: Vec<syn::Path>,
}
impl<'a> EventContentEnumVariation<'a> {
fn new(inner: &'a EventEnum<'a>, variation: EventContentVariation) -> Self {
let kind = inner.kind;
let event_content_types =
inner.events.iter().map(|event| event.to_event_content_path(kind, variation)).collect();
Self {
inner,
variation,
ident: format_ident!("Any{variation}{kind}Content"),
event_content_types,
}
}
}
impl EventContentEnumVariation<'_> {
fn expand(&self) -> TokenStream {
let ruma_events = self.ruma_events;
let serde = ruma_events.reexported(RumaEventsReexport::Serde);
let attrs = &self.attrs;
let ident = &self.ident;
let variants = &self.variants;
let variant_attrs = &self.variant_attrs;
let variant_docs = &self.variant_docs;
let event_content_types = &self.event_content_types;
let custom_content_struct = &self.custom_content_struct;
let event_content_from_type_impl = self.expand_content_enum_event_content_from_type_impl();
let event_content_kind_trait_impl =
self.expand_content_enum_event_content_kind_trait_impl();
let from_impl = self.expand_from_impl(ident, event_content_types);
let json_castable_impl = self.expand_content_enum_json_castable_impl();
let serialize_custom_event_error_path =
quote! { #ruma_events::serialize_custom_event_error }.to_string();
quote! {
#( #attrs )*
#[derive(Clone, Debug, #serde::Serialize)]
#[serde(untagged)]
#[allow(clippy::large_enum_variant, unused_qualifications)]
#[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
pub enum #ident {
#(
#variant_docs
#( #variant_attrs )*
#variants(#event_content_types),
)*
#[doc(hidden)]
#[serde(serialize_with = #serialize_custom_event_error_path)]
_Custom(#ruma_events::_custom::#custom_content_struct),
}
#event_content_from_type_impl
#event_content_kind_trait_impl
#from_impl
#json_castable_impl
}
}
fn expand_content_enum_event_content_from_type_impl(&self) -> TokenStream {
let ruma_events = self.ruma_events;
let serde_json = ruma_events.reexported(RumaEventsReexport::SerdeJson);
let ident = &self.ident;
let variants = &self.variants;
let event_attrs = &self.variant_attrs;
let event_content_types = &self.event_content_types;
let event_type_string_match_arms = &self.event_type_string_match_arms;
let custom_content_struct = &self.custom_content_struct;
let deserialize_event_contents = self.events.iter().zip(event_content_types.iter()).map(
|(event, event_content_type)| {
if event.has_type_fragment() {
quote! {
#event_content_type::from_parts(event_type, json)?
}
} else {
quote! {
#serde_json::from_str(json.get())?
}
}
},
);
quote! {
#[automatically_derived]
impl #ruma_events::EventContentFromType for #ident {
fn from_parts(event_type: &str, json: &#serde_json::value::RawValue) -> serde_json::Result<Self> {
match event_type {
#(
#( #event_attrs )*
#( #event_type_string_match_arms )|* => {
let content = #deserialize_event_contents;
Ok(Self::#variants(content))
},
)*
_ => {
Ok(Self::_Custom(
#ruma_events::_custom::#custom_content_struct::from_parts(event_type, json)?
))
}
}
}
}
}
}
fn expand_content_enum_event_content_kind_trait_impl(&self) -> TokenStream {
let ruma_events = self.ruma_events;
let ident = &self.ident;
let event_type_enum = &self.event_type_enum;
let variants = &self.variants;
let variant_attrs = &self.variant_attrs;
let event_content_kind_trait =
self.kind.to_content_kind_trait(self.variation.to_event_content_trait());
let extra_event_content_impl = (self.kind == EventEnumKind::State).then(|| {
quote! {
type StateKey = String;
}
});
quote! {
#[automatically_derived]
impl #ruma_events::#event_content_kind_trait for #ident {
#extra_event_content_impl
fn event_type(&self) -> #ruma_events::#event_type_enum {
match self {
#(
#( #variant_attrs )*
Self::#variants(content) => content.event_type(),
)*
Self::_Custom(content) => content.event_type(),
}
}
}
}
}
fn expand_content_enum_json_castable_impl(&self) -> TokenStream {
let ruma_common = self.ruma_events.ruma_common();
let ident = &self.ident;
let mut json_castable_impls = quote! {
#[automatically_derived]
impl #ruma_common::serde::JsonCastable<#ruma_common::serde::JsonObject> for #ident {}
};
json_castable_impls.extend(
self.event_content_types.iter().zip(self.variant_attrs.iter()).map(
|(event_content_type, variant_attrs)| {
quote! {
#[allow(unused_qualifications)]
#[automatically_derived]
#( #variant_attrs )*
impl #ruma_common::serde::JsonCastable<#ident> for #event_content_type {}
}
},
),
);
json_castable_impls
}
pub(super) fn expand_content_accessors(
&self,
event_variation: EventVariation,
event_struct: &syn::Ident,
) -> TokenStream {
let ruma_events = self.ruma_events;
let ident = &self.ident;
let variants = &self.variants;
let variant_attrs = &self.variant_attrs;
match (self.kind, self.variation, event_variation) {
(
EventEnumKind::State | EventEnumKind::MessageLike,
EventContentVariation::Original,
EventVariation::None | EventVariation::Sync,
) => {
quote! {
pub fn original_content(&self) -> Option<#ident> {
match self {
#(
#( #variant_attrs )*
Self::#variants(event) => {
event.as_original().map(|ev| #ident::#variants(ev.content.clone()))
}
)*
Self::_Custom(event) => event.as_original().map(|ev| {
#ident::_Custom(ev.content.clone())
}),
}
}
pub fn is_redacted(&self) -> bool {
match self {
#(
#( #variant_attrs )*
Self::#variants(event) => {
event.as_original().is_none()
}
)*
Self::_Custom(event) => event.as_original().is_none(),
}
}
}
}
(
EventEnumKind::State,
EventContentVariation::PossiblyRedacted,
EventVariation::None | EventVariation::Sync,
) => {
quote! {
pub fn content(&self) -> #ident {
match self {
#(
#( #variant_attrs )*
Self::#variants(event) => match event {
#ruma_events::#event_struct::Original(ev) => {
#ident::#variants(ev.content.clone().into())
}
#ruma_events::#event_struct::Redacted(ev) => {
#ident::#variants(ev.content.clone().into())
}
},
)*
Self::_Custom(event) => match event {
#ruma_events::#event_struct::Original(ev) => {
#ident::_Custom(ev.content.clone())
}
#ruma_events::#event_struct::Redacted(ev) => {
#ident::_Custom(ev.content.clone())
}
},
}
}
}
}
_ => {
quote! {
pub fn content(&self) -> #ident {
match self {
#(
#( #variant_attrs )*
Self::#variants(event) => #ident::#variants(event.content.clone()),
)*
Self::_Custom(event) => #ident::_Custom(event.content.clone()),
}
}
}
}
}
}
}
impl<'a> Deref for EventContentEnumVariation<'a> {
type Target = EventEnum<'a>;
fn deref(&self) -> &Self::Target {
self.inner
}
}
pub(super) struct EventContentChangeEnum<'a> {
inner: &'a EventEnum<'a>,
ident: syn::Ident,
event_content_types: Vec<syn::Path>,
}
impl<'a> EventContentChangeEnum<'a> {
fn new(inner: &'a EventEnum<'a>) -> Self {
let ident = syn::Ident::new("AnyStateEventContentChange", Span::call_site());
let kind = inner.kind;
let event_content_types = inner
.events
.iter()
.map(|event| event.to_event_content_path(kind, EventContentVariation::Original))
.collect();
Self { inner, ident, event_content_types }
}
}
impl EventContentChangeEnum<'_> {
fn expand(&self) -> TokenStream {
let ruma_events = self.ruma_events;
let attrs = &self.attrs;
let ident = &self.ident;
let event_type_enum = &self.event_type_enum;
let variants = &self.variants;
let variant_attrs = &self.variant_attrs;
let variant_docs = &self.variant_docs;
let event_content_types = &self.event_content_types;
let custom_content_struct = &self.custom_content_struct;
let event_content_kind_trait =
self.kind.to_content_kind_trait(EventContentTraitVariation::Original);
quote! {
#( #attrs )*
#[derive(Clone, Debug)]
#[allow(clippy::large_enum_variant, unused_qualifications)]
#[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
pub enum #ident {
#(
#variant_docs
#( #variant_attrs )*
#variants(#ruma_events::StateEventContentChange<#event_content_types>),
)*
#[doc(hidden)]
_Custom(#ruma_events::_custom::#custom_content_struct),
}
impl #ident {
pub fn event_type(&self) -> #ruma_events::#event_type_enum {
match self {
#(
#( #variant_attrs )*
Self::#variants(content) => content.event_type(),
)*
Self::_Custom(content) => {
#ruma_events::#event_content_kind_trait::event_type(content)
}
}
}
}
}
}
pub(super) fn expand_content_accessors(&self, event_enum: &syn::Ident) -> TokenStream {
let ruma_events = self.ruma_events;
let ident = &self.ident;
let variants = &self.variants;
let variant_attrs = &self.variant_attrs;
quote! {
pub fn content_change(&self) -> #ident {
match self {
#(
#( #variant_attrs )*
Self::#variants(event) => match event {
#ruma_events::#event_enum::Original(ev) => #ident::#variants(
#ruma_events::StateEventContentChange::Original {
content: ev.content.clone(),
prev_content: ev.unsigned.prev_content.clone()
}
),
#ruma_events::#event_enum::Redacted(ev) => #ident::#variants(
#ruma_events::StateEventContentChange::Redacted(
ev.content.clone()
)
),
},
)*
Self::_Custom(event) => match event {
#ruma_events::#event_enum::Original(ev) => {
#ident::_Custom(ev.content.clone())
}
#ruma_events::#event_enum::Redacted(ev) => {
#ident::_Custom(ev.content.clone())
}
},
}
}
}
}
}
impl<'a> Deref for EventContentChangeEnum<'a> {
type Target = EventEnum<'a>;
fn deref(&self) -> &Self::Target {
self.inner
}
}