diff --git a/crates/ruma-common/CHANGELOG.md b/crates/ruma-common/CHANGELOG.md index e9fc781a..56ab1a47 100644 --- a/crates/ruma-common/CHANGELOG.md +++ b/crates/ruma-common/CHANGELOG.md @@ -62,6 +62,7 @@ Improvements: * `Ruleset::set_actions` to change the actions of push rules * Add support for bundled reference relations (MSC3267 / Matrix 1.5) * Add the `formatted` field on `KeyVerificationRequestEventContent` (Matrix 1.5) +* Add `content` accessors for `Any*StateEvent` enums # 0.10.5 diff --git a/crates/ruma-common/src/events/kinds.rs b/crates/ruma-common/src/events/kinds.rs index a5ea01c1..282d83c4 100644 --- a/crates/ruma-common/src/events/kinds.rs +++ b/crates/ruma-common/src/events/kinds.rs @@ -417,6 +417,28 @@ pub struct DecryptedMegolmV1Event { pub room_id: OwnedRoomId, } +/// A possibly-redacted state event content. +/// +/// A non-redacted content also contains the `prev_content` from the unsigned event data. +#[allow(clippy::exhaustive_enums)] +#[derive(Clone, Debug)] +pub enum FullStateEventContent +where + C::Redacted: RedactedStateEventContent, +{ + /// Original, unredacted content of the event. + Original { + /// Current content of the room state. + content: C, + + /// Previous content of the room state. + prev_content: Option, + }, + + /// Redacted content of the event. + Redacted(C::Redacted), +} + macro_rules! impl_possibly_redacted_event { ( $ty:ident ( $content_trait:ident, $redacted_content_trait:ident, $event_type:ident ) diff --git a/crates/ruma-macros/src/events/event_enum.rs b/crates/ruma-macros/src/events/event_enum.rs index 1d92eb76..07dd7d31 100644 --- a/crates/ruma-macros/src/events/event_enum.rs +++ b/crates/ruma-macros/src/events/event_enum.rs @@ -87,6 +87,7 @@ pub fn expand_event_enums(input: &EventEnumDecl) -> syn::Result { } if matches!(kind, EventKind::State) { + res.extend(expand_full_content_enum(kind, events, docs, attrs, variants, ruma_common)); res.extend( expand_event_enum(kind, V::Stripped, events, docs, attrs, variants, ruma_common) .unwrap_or_else(syn::Error::into_compile_error), @@ -125,7 +126,8 @@ fn expand_event_enum( let custom_ty = format_ident!("Custom{}Content", kind); let deserialize_impl = expand_deserialize_impl(kind, var, events, ruma_common)?; - let field_accessor_impl = expand_accessor_methods(kind, var, variants, ruma_common)?; + let field_accessor_impl = + expand_accessor_methods(kind, var, variants, &event_struct, ruma_common)?; let from_impl = expand_from_impl(&ident, &content, variants); Ok(quote! { @@ -419,10 +421,51 @@ fn expand_content_enum( }) } +/// Create a full content enum from `EventEnumInput`. +fn expand_full_content_enum( + kind: EventKind, + events: &[EventEnumEntry], + docs: &[TokenStream], + attrs: &[Attribute], + variants: &[EventEnumVariant], + ruma_common: &TokenStream, +) -> syn::Result { + let ident = kind.to_full_content_enum(); + + let content: Vec<_> = events + .iter() + .map(|event| { + let stable_name = event.stable_name()?; + Ok(to_event_content_path(kind, stable_name, &event.ev_path, None)) + }) + .collect::>()?; + + let variant_decls = variants.iter().map(|v| v.decl()).collect::>(); + + Ok(quote! { + #( #attrs )* + #[derive(Clone, Debug)] + #[allow(clippy::large_enum_variant)] + #[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)] + pub enum #ident { + #( + #docs + #variant_decls(#ruma_common::events::FullStateEventContent<#content>), + )* + #[doc(hidden)] + _Custom { + event_type: crate::PrivOwnedStr, + redacted: bool, + }, + } + }) +} + fn expand_accessor_methods( kind: EventKind, var: EventEnumVariation, variants: &[EventEnumVariant], + event_struct: &Ident, ruma_common: &TokenStream, ) -> syn::Result { let ident = kind.to_event_enum_ident(var.into())?; @@ -450,7 +493,7 @@ fn expand_accessor_methods( let content_enum = kind.to_content_enum(); let content_variants: Vec<_> = variants.iter().map(|v| v.ctor(&content_enum)).collect(); let content_accessor = if maybe_redacted { - quote! { + let mut accessors = quote! { /// Returns the content for this event if it is not redacted, or `None` if it is. pub fn original_content(&self) -> Option<#content_enum> { match self { @@ -474,7 +517,66 @@ fn expand_accessor_methods( }), } } + }; + + if kind == EventKind::State { + let full_content_enum = kind.to_full_content_enum(); + let full_content_variants: Vec<_> = + variants.iter().map(|v| v.ctor(&full_content_enum)).collect(); + + accessors = quote! { + #accessors + + /// Returns the content of this state event. + pub fn content(&self) -> #full_content_enum { + match self { + #( + #self_variants(event) => match event { + #ruma_common::events::#event_struct::Original(ev) => #full_content_variants( + #ruma_common::events::FullStateEventContent::Original { + content: ev.content.clone(), + prev_content: ev.unsigned.prev_content.clone() + } + ), + #ruma_common::events::#event_struct::Redacted(ev) => #full_content_variants( + #ruma_common::events::FullStateEventContent::Redacted( + ev.content.clone() + ) + ), + } + )* + Self::_Custom(event) => match event { + #ruma_common::events::#event_struct::Original(ev) => { + #full_content_enum::_Custom { + event_type: crate::PrivOwnedStr( + ::std::string::ToString::to_string( + &#ruma_common::events::EventContent::event_type( + &ev.content, + ), + ).into_boxed_str(), + ), + redacted: false, + } + } + #ruma_common::events::#event_struct::Redacted(ev) => { + #full_content_enum::_Custom { + event_type: crate::PrivOwnedStr( + ::std::string::ToString::to_string( + &#ruma_common::events::EventContent::event_type( + &ev.content, + ), + ).into_boxed_str(), + ), + redacted: true, + } + } + }, + } + } + }; } + + accessors } else { quote! { /// Returns the content for this event. diff --git a/crates/ruma-macros/src/events/event_parse.rs b/crates/ruma-macros/src/events/event_parse.rs index b8e382d0..280bc892 100644 --- a/crates/ruma-macros/src/events/event_parse.rs +++ b/crates/ruma-macros/src/events/event_parse.rs @@ -147,6 +147,11 @@ impl EventKind { pub fn to_content_enum(self) -> Ident { format_ident!("Any{}Content", self) } + + /// `AnyFull[kind]EventContent` + pub fn to_full_content_enum(self) -> Ident { + format_ident!("AnyFull{}Content", self) + } } impl Parse for EventKind {