events: Generate redact method for content enums

… and remove redacted content structs non-room events
This commit is contained in:
Devin Ragotzy 2021-05-15 08:51:20 -04:00 committed by GitHub
parent 4aec2d5ff0
commit 34134267c6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 143 additions and 29 deletions

View File

@ -129,7 +129,8 @@ pub fn expand_event_content(
let content_derives =
content_attr.iter().flat_map(|args| args.get_event_kinds()).collect::<Vec<_>>();
let redacted = if needs_redacted(&content_attr) {
// We only generate redacted content structs for state and message events
let redacted = if needs_redacted(&content_attr, &content_derives) {
let doc = format!("The payload for a redacted `{}`", ident);
let redacted_ident = format_ident!("Redacted{}", ident);
let kept_redacted_fields = if let syn::Data::Struct(syn::DataStruct {
@ -358,9 +359,10 @@ fn generate_event_content_impl(
}
}
fn needs_redacted(input: &[MetaAttrs]) -> bool {
fn needs_redacted(input: &[MetaAttrs], content_derives: &[&EventKind]) -> bool {
// `is_custom` means that the content struct does not need a generated
// redacted struct also. If no `custom_redacted` attrs are found the content
// needs a redacted struct generated.
!input.iter().any(|a| a.is_custom())
&& content_derives.iter().any(|e| e.is_message() || e.is_state())
}

View File

@ -364,16 +364,18 @@ fn expand_content_enum(
variants: &[EventEnumVariant],
ruma_events: &TokenStream,
) -> TokenStream {
let ruma_identifiers = quote! { #ruma_events::exports::ruma_identifiers };
let serde = quote! { #ruma_events::exports::serde };
let serde_json = quote! { #ruma_events::exports::serde_json };
let ident = kind.to_content_enum();
let event_type_str = events;
let content: Vec<_> =
events.iter().map(|ev| to_event_content_path(kind, ev, ruma_events)).collect();
events.iter().map(|ev| to_event_content_path(kind, ev, None, ruma_events)).collect();
let variant_decls = variants.iter().map(|v| v.decl());
let variant_decls = variants.iter().map(|v| v.decl()).collect::<Vec<_>>();
let content_enum = quote! {
#( #attrs )*
@ -394,7 +396,7 @@ fn expand_content_enum(
let attrs = &v.attrs;
quote! { #(#attrs)* }
});
let variant_arms = variants.iter().map(|v| v.match_arm(quote!(Self)));
let variant_arms = variants.iter().map(|v| v.match_arm(quote!(Self))).collect::<Vec<_>>();
let variant_ctors = variants.iter().map(|v| v.ctor(quote!(Self)));
let event_content_impl = quote! {
@ -430,12 +432,65 @@ fn expand_content_enum(
let marker_trait_impls = marker_traits(kind, ruma_events);
let redacted_content_enum = if kind.is_state() || kind.is_message() {
let redacted_ident = kind.to_redacted_content_enum();
let redaction_variants = variants.iter().map(|v| v.ctor(&redacted_ident));
let redacted_content: Vec<_> = events
.iter()
.map(|ev| to_event_content_path(kind, ev, Some("Redacted"), ruma_events))
.collect();
quote! {
#( #attrs )*
#[derive(Clone, Debug, #serde::Serialize)]
#[serde(untagged)]
#[allow(clippy::large_enum_variant)]
pub enum #redacted_ident {
#(
#[doc = #event_type_str]
#variant_decls(#redacted_content),
)*
/// Content of a redacted event not defined by the Matrix specification.
Custom(#ruma_events::custom::RedactedCustomEventContent),
}
impl #ruma_events::RedactContent for #ident {
type Redacted = #redacted_ident;
/// Redacts `Self` given a `RoomVersionId`.
fn redact(
self,
version: &#ruma_identifiers::RoomVersionId,
) -> #redacted_ident {
match self {
#(
#variant_arms(content) => {
#redaction_variants(
#ruma_events::RedactContent::redact(content, version)
)
},
)*
Self::Custom(content) => {
#redacted_ident::Custom(
#ruma_events::RedactContent::redact(content, version)
)
},
}
}
}
}
} else {
TokenStream::new()
};
quote! {
#content_enum
#event_content_impl
#marker_trait_impls
#redacted_content_enum
}
}
@ -859,6 +914,7 @@ fn to_event_path(name: &LitStr, struct_name: &Ident, ruma_events: &TokenStream)
fn to_event_content_path(
kind: &EventKind,
name: &LitStr,
prefix: Option<&str>,
ruma_events: &TokenStream,
) -> TokenStream {
let span = name.span();
@ -876,8 +932,10 @@ fn to_event_content_path(
.collect();
let content_str = match kind {
EventKind::ToDevice => format_ident!("{}ToDeviceEventContent", event),
_ => format_ident!("{}EventContent", event),
EventKind::ToDevice => {
format_ident!("{}{}ToDeviceEventContent", prefix.unwrap_or(""), event)
}
_ => format_ident!("{}{}EventContent", prefix.unwrap_or(""), event),
};
let path = path.iter().map(|s| Ident::new(s, span));

View File

@ -3,7 +3,7 @@
use std::fmt;
use proc_macro2::Span;
use quote::format_ident;
use quote::{format_ident, IdentFragment};
use syn::{
braced,
parse::{self, Parse, ParseStream},
@ -88,6 +88,26 @@ impl fmt::Display for EventKind {
}
}
impl IdentFragment for EventKind {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Display::fmt(self, f)
}
fn span(&self) -> Option<Span> {
Some(Span::call_site())
}
}
impl IdentFragment for EventKindVariation {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Display::fmt(self, f)
}
fn span(&self) -> Option<Span> {
Some(Span::call_site())
}
}
impl EventKind {
pub fn is_state(&self) -> bool {
matches!(self, Self::State)
@ -112,9 +132,7 @@ impl EventKind {
| (Self::State, V::Redacted)
| (Self::Message, V::RedactedSync)
| (Self::State, V::RedactedSync)
| (Self::State, V::RedactedStripped) => {
Some(Ident::new(&format!("{}{}", var, self), Span::call_site()))
}
| (Self::State, V::RedactedStripped) => Some(format_ident!("{}{}", var, self)),
_ => None,
}
}
@ -125,7 +143,12 @@ impl EventKind {
/// `Any[kind]EventContent`
pub fn to_content_enum(&self) -> Ident {
Ident::new(&format!("Any{}Content", self), Span::call_site())
format_ident!("Any{}Content", self)
}
/// `AnyRedacted[kind]EventContent`
pub fn to_redacted_content_enum(&self) -> Ident {
format_ident!("AnyRedacted{}Content", self)
}
}

View File

@ -82,8 +82,8 @@
//! }
//! });
//!
//! // The downside of this event is we cannot use it with event enums, but could be deserialized
//! // from a `Raw<_>` that has failed to deserialize.
//! // The downside of this event is we cannot use it with event enums,
//! // but could be deserialized from a `Raw<_>` that has failed to deserialize.
//! matches::assert_matches!(
//! serde_json::from_value::<SyncMessageEvent<ReactionEventContent>>(json),
//! Ok(SyncMessageEvent {

View File

@ -5,10 +5,7 @@ use ruma_events_macros::{Event, EventContent};
use ruma_identifiers::{EventId, RoomId, UserId};
use serde::{Deserialize, Serialize};
use crate::{
MessageEventContent, RedactedMessageEventContent, RedactedStateEventContent, RoomEventContent,
Unsigned,
};
use crate::{RedactedStateEventContent, Unsigned};
/// Redaction event.
#[derive(Clone, Debug, Event)]
@ -59,17 +56,11 @@ pub struct SyncRedactionEvent {
/// A redaction of an event.
#[derive(Clone, Debug, Deserialize, Serialize, EventContent)]
#[ruma_event(type = "m.room.redaction")]
#[ruma_event(type = "m.room.redaction", kind = Message)]
pub struct RedactionEventContent {
/// The reason for the redaction, if any.
#[serde(skip_serializing_if = "Option::is_none")]
pub reason: Option<String>,
}
impl RoomEventContent for RedactionEventContent {}
impl MessageEventContent for RedactionEventContent {}
impl RedactedMessageEventContent for RedactedRedactionEventContent {}
impl RedactedStateEventContent for RedactedRedactionEventContent {}

View File

@ -9,13 +9,17 @@ use ruma_events::{
message::RedactedMessageEventContent,
redaction::{RedactionEventContent, SyncRedactionEvent},
},
AnyMessageEvent, AnyRedactedMessageEvent, AnyRedactedSyncMessageEvent,
AnyRedactedSyncStateEvent, AnyRoomEvent, AnySyncRoomEvent, Redact, RedactedMessageEvent,
RedactedSyncMessageEvent, RedactedSyncStateEvent, RedactedUnsigned, Unsigned,
AnyMessageEvent, AnyMessageEventContent, AnyRedactedMessageEvent,
AnyRedactedMessageEventContent, AnyRedactedStateEventContent, AnyRedactedSyncMessageEvent,
AnyRedactedSyncStateEvent, AnyRoomEvent, AnyStateEventContent, AnySyncRoomEvent, EventContent,
Redact, RedactContent, RedactedMessageEvent, RedactedSyncMessageEvent, RedactedSyncStateEvent,
RedactedUnsigned, Unsigned,
};
use ruma_identifiers::{event_id, room_id, user_id, RoomVersionId};
use ruma_serde::Raw;
use serde_json::{from_value as from_json_value, json, to_value as to_json_value};
use serde_json::{
from_value as from_json_value, json, to_value as to_json_value, value::to_raw_value,
};
fn unsigned() -> RedactedUnsigned {
let mut unsigned = RedactedUnsigned::default();
@ -332,3 +336,39 @@ fn redact_method_properly_redacts() {
&& origin_server_ts == MilliSecondsSinceUnixEpoch(uint!(1))
);
}
#[test]
fn redact_message_content() {
let json = json!({
"body": "test",
"msgtype": "m.audio",
"url": "mxc://example.com/AuDi0",
});
let content =
AnyMessageEventContent::from_parts("m.room.message", to_raw_value(&json).unwrap()).unwrap();
assert_matches!(
content.redact(&RoomVersionId::Version6),
AnyRedactedMessageEventContent::RoomMessage(RedactedMessageEventContent)
);
}
#[test]
fn redact_state_content() {
let json = json!({
"creator": "@carl:example.com",
"m.federate": true,
"room_version": "4"
});
let content =
AnyStateEventContent::from_parts("m.room.create", to_raw_value(&json).unwrap()).unwrap();
assert_matches!(
content.redact(&RoomVersionId::Version6),
AnyRedactedStateEventContent::RoomCreate(RedactedCreateEventContent {
creator
}) if creator == user_id!("@carl:example.com")
);
}