events: Generate redact method for content enums
… and remove redacted content structs non-room events
This commit is contained in:
parent
4aec2d5ff0
commit
34134267c6
@ -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())
|
||||
}
|
||||
|
@ -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));
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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 {
|
||||
|
@ -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 {}
|
||||
|
@ -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")
|
||||
);
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user