use proc_macro2::Span;
use syn::meta::ParseNestedMeta;
use super::{Event, EventField, EventKind};
use crate::{
events::common::EventVariation,
util::{ParseNestedMetaExt, RumaEvents},
};
impl Event {
pub(super) fn parse(input: syn::DeriveInput) -> syn::Result<Self> {
let (kind, variation) = event_ident_to_kind_and_variation(&input.ident)?;
let syn::Data::Struct(syn::DataStruct {
fields: syn::Fields::Named(syn::FieldsNamed { named, .. }),
..
}) = input.data
else {
return Err(syn::Error::new_spanned(
&input.ident,
"the `Event` derive only supports structs with named fields",
));
};
let fields = named.into_iter().map(EventField::parse).collect::<Result<_, _>>()?;
let event = Self {
ident: input.ident,
generics: input.generics,
kind,
variation,
fields,
ruma_events: RumaEvents::new(),
};
event.validate()?;
Ok(event)
}
fn validate(&self) -> syn::Result<()> {
let Some(content_field) = self.fields.iter().find(|field| *field.ident() == "content")
else {
return Err(syn::Error::new(
Span::call_site(),
"`Event` struct must contain at least a `content` field",
));
};
if !self.generics.params.is_empty() && (!self.generics.params.iter().any(|param| matches!(param, syn::GenericParam::Type(syn::TypeParam { ident, ..}) if *ident == "C")) || !matches!(&content_field.inner.ty, syn::Type::Path(syn::TypePath{ path, ..}) if path.is_ident("C"))) {
return Err(syn::Error::new(
Span::call_site(),
"Generics on `Event` struct are only supported for \
the `content` field and must be named `C`",
));
}
Ok(())
}
}
fn event_ident_to_kind_and_variation(
ident: &syn::Ident,
) -> syn::Result<(EventKind, EventVariation)> {
Ok(match ident.to_string().as_str() {
"GlobalAccountDataEvent" => (EventKind::GlobalAccountData, EventVariation::None),
"RoomAccountDataEvent" => (EventKind::RoomAccountData, EventVariation::None),
"EphemeralRoomEvent" => (EventKind::EphemeralRoom, EventVariation::None),
"SyncEphemeralRoomEvent" => (EventKind::EphemeralRoom, EventVariation::Sync),
"OriginalMessageLikeEvent" => (EventKind::MessageLike, EventVariation::Original),
"OriginalSyncMessageLikeEvent" => (EventKind::MessageLike, EventVariation::OriginalSync),
"RedactedMessageLikeEvent" => (EventKind::MessageLike, EventVariation::Redacted),
"RedactedSyncMessageLikeEvent" => (EventKind::MessageLike, EventVariation::RedactedSync),
"OriginalStateEvent" => (EventKind::State, EventVariation::Original),
"OriginalSyncStateEvent" => (EventKind::State, EventVariation::OriginalSync),
"StrippedStateEvent" => (EventKind::State, EventVariation::Stripped),
"InitialStateEvent" => (EventKind::State, EventVariation::Initial),
"RedactedStateEvent" => (EventKind::State, EventVariation::Redacted),
"RedactedSyncStateEvent" => (EventKind::State, EventVariation::RedactedSync),
"ToDeviceEvent" => (EventKind::ToDevice, EventVariation::None),
"HierarchySpaceChildEvent" => (EventKind::HierarchySpaceChild, EventVariation::Stripped),
"OriginalRoomRedactionEvent" => (EventKind::RoomRedaction, EventVariation::None),
"OriginalSyncRoomRedactionEvent" => {
(EventKind::RoomRedaction, EventVariation::OriginalSync)
}
"RedactedRoomRedactionEvent" => (EventKind::RoomRedaction, EventVariation::Redacted),
"RedactedSyncRoomRedactionEvent" => {
(EventKind::RoomRedaction, EventVariation::RedactedSync)
}
"DecryptedOlmV1Event" | "DecryptedMegolmV1Event" => {
(EventKind::Decrypted, EventVariation::None)
}
_ => {
return Err(syn::Error::new_spanned(
ident,
"not a supported `Event` struct identifier",
));
}
})
}
impl EventField {
fn parse(mut inner: syn::Field) -> syn::Result<Self> {
let mut field_attrs = EventFieldAttrs::default();
for attr in inner.attrs.extract_if(.., |attribute| attribute.path().is_ident("ruma_event"))
{
attr.parse_nested_meta(|meta| field_attrs.try_merge(meta, &attr))?;
}
let EventFieldAttrs { default, default_on_error, rename, aliases } = field_attrs;
Ok(Self { inner, default, default_on_error, rename, aliases })
}
}
#[derive(Default)]
struct EventFieldAttrs {
default: bool,
default_on_error: bool,
rename: Option<syn::LitStr>,
aliases: Vec<syn::LitStr>,
}
impl EventFieldAttrs {
fn set_default(&mut self, attr: &syn::Attribute) -> syn::Result<()> {
if self.default {
return Err(syn::Error::new_spanned(
attr,
"cannot have multiple occurrences of `default` attribute",
));
}
self.default = true;
Ok(())
}
fn set_default_on_error(&mut self, attr: &syn::Attribute) -> syn::Result<()> {
if self.default_on_error {
return Err(syn::Error::new_spanned(
attr,
"cannot have multiple occurrences of `default_on_error` attribute",
));
}
self.default_on_error = true;
Ok(())
}
fn set_rename(&mut self, rename: syn::LitStr, attr: &syn::Attribute) -> syn::Result<()> {
if self.rename.is_some() {
return Err(syn::Error::new_spanned(
attr,
"cannot have multiple values for `rename` attribute",
));
}
self.rename = Some(rename);
Ok(())
}
fn try_merge(&mut self, meta: ParseNestedMeta<'_>, attr: &syn::Attribute) -> syn::Result<()> {
if meta.path.is_ident("default") {
if meta.has_value() {
return Err(meta.error("`default` attribute doesn't expect a value"));
}
return self.set_default(attr);
}
if meta.path.is_ident("default_on_error") {
if meta.has_value() {
return Err(meta.error("`default_on_error` attribute doesn't expect a value"));
}
return self.set_default_on_error(attr);
}
if meta.path.is_ident("rename") {
return self.set_rename(meta.value()?.parse()?, attr);
}
if meta.path.is_ident("alias") {
self.aliases.push(meta.value()?.parse()?);
return Ok(());
}
Err(meta.error("unsupported `ruma_event` attribute"))
}
}