events: Rework the content traits

This commit is contained in:
Jonas Platte 2023-01-16 18:59:25 +01:00 committed by Jonas Platte
parent 49a0650f62
commit 2eba14f0cc
No known key found for this signature in database
GPG Key ID: 7D261D771D915378
11 changed files with 121 additions and 99 deletions

View File

@ -4,10 +4,10 @@ use serde_json::value::RawValue as RawJsonValue;
use super::{
EphemeralRoomEventContent, EphemeralRoomEventType, EventContent, EventContentFromType,
GlobalAccountDataEventContent, GlobalAccountDataEventType, MessageLikeEventContent,
MessageLikeEventType, MessageLikeUnsigned, OriginalStateEventContent, RedactContent,
MessageLikeEventType, MessageLikeUnsigned, PossiblyRedactedStateEventContent, RedactContent,
RedactedMessageLikeEventContent, RedactedStateEventContent, RoomAccountDataEventContent,
RoomAccountDataEventType, StateEventContent, StateEventType, ToDeviceEventContent,
ToDeviceEventType,
RoomAccountDataEventType, StateEventContent, StateEventType, StaticStateEventContent,
ToDeviceEventContent, ToDeviceEventType,
};
use crate::RoomVersionId;
@ -70,14 +70,19 @@ custom_room_event_content!(CustomStateEventContent, StateEventType);
impl StateEventContent for CustomStateEventContent {
type StateKey = String;
}
impl OriginalStateEventContent for CustomStateEventContent {
impl StaticStateEventContent for CustomStateEventContent {
// Like `StateUnsigned`, but without `prev_content`.
// We don't care about `prev_content` since we'd only store the event type that is the same
// as in the content.
type Unsigned = MessageLikeUnsigned;
type PossiblyRedacted = Self;
}
impl RedactedStateEventContent for CustomStateEventContent {}
impl PossiblyRedactedStateEventContent for CustomStateEventContent {
type StateKey = String;
}
impl RedactedStateEventContent for CustomStateEventContent {
type StateKey = String;
}
custom_event_content!(CustomToDeviceEventContent, ToDeviceEventType);
impl ToDeviceEventContent for CustomToDeviceEventContent {}

View File

@ -6,7 +6,7 @@ use serde_json::{from_str as from_json_str, value::RawValue as RawJsonValue};
use crate::serde::{CanBeEmpty, Raw};
use super::{
EphemeralRoomEventType, GlobalAccountDataEventType, MessageLikeEventType, RedactContent,
EphemeralRoomEventType, GlobalAccountDataEventType, MessageLikeEventType,
RoomAccountDataEventType, StateEventType, ToDeviceEventType,
};
@ -52,29 +52,38 @@ pub trait RoomAccountDataEventContent: EventContent<EventType = RoomAccountDataE
/// Content of an ephemeral room event.
pub trait EphemeralRoomEventContent: EventContent<EventType = EphemeralRoomEventType> {}
/// Content of a message-like event.
/// Content of a non-redacted message-like event.
pub trait MessageLikeEventContent: EventContent<EventType = MessageLikeEventType> {}
/// Content of a redacted message-like event.
pub trait RedactedMessageLikeEventContent: MessageLikeEventContent {}
pub trait RedactedMessageLikeEventContent: EventContent<EventType = MessageLikeEventType> {}
/// Content of a state event.
/// Content of a non-redacted state event.
pub trait StateEventContent: EventContent<EventType = StateEventType> {
/// The type of the event's `state_key` field.
type StateKey: AsRef<str> + Clone + fmt::Debug + DeserializeOwned + Serialize;
}
/// Content of a non-redacted state event.
pub trait OriginalStateEventContent: StateEventContent + RedactContent {
/// Content of a non-redacted state event with a corresponding possibly-redacted type.
pub trait StaticStateEventContent: StateEventContent {
/// The possibly redacted form of the event's content.
type PossiblyRedacted: PossiblyRedactedStateEventContent;
/// The type of the event's `unsigned` field.
type Unsigned: Clone + fmt::Debug + Default + CanBeEmpty + DeserializeOwned;
/// The possibly redacted form of the event's content.
type PossiblyRedacted: StateEventContent;
}
/// Content of a redacted state event.
pub trait RedactedStateEventContent: StateEventContent {}
pub trait RedactedStateEventContent: EventContent<EventType = StateEventType> {
/// The type of the event's `state_key` field.
type StateKey: AsRef<str> + Clone + fmt::Debug + DeserializeOwned + Serialize;
}
/// Content of a state event.
pub trait PossiblyRedactedStateEventContent: EventContent<EventType = StateEventType> {
/// The type of the event's `state_key` field.
type StateKey: AsRef<str> + Clone + fmt::Debug + DeserializeOwned + Serialize;
}
/// Content of a to-device event.
pub trait ToDeviceEventContent: EventContent<EventType = ToDeviceEventType> {}

View File

@ -6,9 +6,9 @@ use serde_json::value::RawValue as RawJsonValue;
use super::{
EphemeralRoomEventContent, EventContent, EventContentFromType, GlobalAccountDataEventContent,
MessageLikeEventContent, MessageLikeEventType, MessageLikeUnsigned, OriginalStateEventContent,
RedactContent, RedactedMessageLikeEventContent, RedactedStateEventContent, RedactedUnsigned,
RedactionDeHelper, RoomAccountDataEventContent, StateEventContent, StateEventType,
MessageLikeEventContent, MessageLikeEventType, MessageLikeUnsigned, RedactContent,
RedactedMessageLikeEventContent, RedactedStateEventContent, RedactedUnsigned,
RedactionDeHelper, RoomAccountDataEventContent, StateEventType, StaticStateEventContent,
ToDeviceEventContent,
};
use crate::{
@ -229,7 +229,7 @@ where
/// `OriginalStateEvent` implements the comparison traits using only the `event_id` field, a sorted
/// list would be sorted lexicographically based on the event's `EventId`.
#[derive(Clone, Debug, Event)]
pub struct OriginalStateEvent<C: OriginalStateEventContent> {
pub struct OriginalStateEvent<C: StaticStateEventContent> {
/// Data specific to the event type.
pub content: C,
@ -260,7 +260,7 @@ pub struct OriginalStateEvent<C: OriginalStateEventContent> {
/// `OriginalSyncStateEvent` implements the comparison traits using only the `event_id` field, a
/// sorted list would be sorted lexicographically based on the event's `EventId`.
#[derive(Clone, Debug, Event)]
pub struct OriginalSyncStateEvent<C: OriginalStateEventContent> {
pub struct OriginalSyncStateEvent<C: StaticStateEventContent> {
/// Data specific to the event type.
pub content: C,
@ -285,7 +285,7 @@ pub struct OriginalSyncStateEvent<C: OriginalStateEventContent> {
/// A stripped-down state event, used for previews of rooms the user has been invited to.
#[derive(Clone, Debug, Event)]
pub struct StrippedStateEvent<C: StateEventContent> {
pub struct StrippedStateEvent<C: StaticStateEventContent> {
/// Data specific to the event type.
pub content: C,
@ -301,7 +301,7 @@ pub struct StrippedStateEvent<C: StateEventContent> {
/// A minimal state event, used for creating a new room.
#[derive(Clone, Debug, Event)]
pub struct InitialStateEvent<C: StateEventContent> {
pub struct InitialStateEvent<C: StaticStateEventContent> {
/// Data specific to the event type.
pub content: C,
@ -379,7 +379,7 @@ pub struct RedactedSyncStateEvent<C: RedactedStateEventContent> {
/// would be sorted lexicographically based on the event's `EventId`.
#[allow(clippy::exhaustive_enums)]
#[derive(Clone, Debug)]
pub enum StateEvent<C: OriginalStateEventContent>
pub enum StateEvent<C: StaticStateEventContent + RedactContent>
where
C::Redacted: RedactedStateEventContent,
{
@ -396,7 +396,7 @@ where
/// would be sorted lexicographically based on the event's `EventId`.
#[allow(clippy::exhaustive_enums)]
#[derive(Clone, Debug)]
pub enum SyncStateEvent<C: OriginalStateEventContent>
pub enum SyncStateEvent<C: StaticStateEventContent + RedactContent>
where
C::Redacted: RedactedStateEventContent,
{
@ -471,10 +471,7 @@ pub struct DecryptedMegolmV1Event<C: MessageLikeEventContent> {
/// A non-redacted content also contains the `prev_content` from the unsigned event data.
#[allow(clippy::exhaustive_enums)]
#[derive(Clone, Debug)]
pub enum FullStateEventContent<C: OriginalStateEventContent>
where
C::Redacted: RedactedStateEventContent,
{
pub enum FullStateEventContent<C: StaticStateEventContent + RedactContent> {
/// Original, unredacted content of the event.
Original {
/// Current content of the room state.
@ -488,7 +485,7 @@ where
Redacted(C::Redacted),
}
impl<C: OriginalStateEventContent> FullStateEventContent<C>
impl<C: StaticStateEventContent + RedactContent> FullStateEventContent<C>
where
C::Redacted: RedactedStateEventContent,
{
@ -617,9 +614,9 @@ impl_possibly_redacted_event!(
);
impl_possibly_redacted_event!(
StateEvent(OriginalStateEventContent, RedactedStateEventContent, StateEventType)
StateEvent(StaticStateEventContent, RedactedStateEventContent, StateEventType)
where
C::Redacted: StateEventContent<StateKey = C::StateKey>,
C::Redacted: RedactedStateEventContent<StateKey = C::StateKey>,
{
/// Returns this event's `room_id` field.
pub fn room_id(&self) -> &RoomId {
@ -648,9 +645,9 @@ impl_possibly_redacted_event!(
);
impl_possibly_redacted_event!(
SyncStateEvent(OriginalStateEventContent, RedactedStateEventContent, StateEventType)
SyncStateEvent(StaticStateEventContent, RedactedStateEventContent, StateEventType)
where
C::Redacted: StateEventContent<StateKey = C::StateKey>,
C::Redacted: RedactedStateEventContent<StateKey = C::StateKey>,
{
/// Returns this event's `state_key` field.
pub fn state_key(&self) -> &C::StateKey {
@ -704,6 +701,6 @@ impl_sync_from_full!(
impl_sync_from_full!(
SyncStateEvent,
StateEvent,
OriginalStateEventContent,
StaticStateEventContent,
RedactedStateEventContent
);

View File

@ -6,7 +6,7 @@ use ruma_macros::EventContent;
use serde::{Deserialize, Serialize};
use super::{PolicyRuleEventContent, PossiblyRedactedPolicyRuleEventContent};
use crate::events::{EventContent, StateEventContent, StateEventType};
use crate::events::{EventContent, PossiblyRedactedStateEventContent, StateEventType};
/// The content of an `m.policy.rule.room` event.
///
@ -31,7 +31,7 @@ impl EventContent for PossiblyRedactedPolicyRuleRoomEventContent {
}
}
impl StateEventContent for PossiblyRedactedPolicyRuleRoomEventContent {
impl PossiblyRedactedStateEventContent for PossiblyRedactedPolicyRuleRoomEventContent {
type StateKey = String;
}

View File

@ -6,7 +6,7 @@ use ruma_macros::EventContent;
use serde::{Deserialize, Serialize};
use super::{PolicyRuleEventContent, PossiblyRedactedPolicyRuleEventContent};
use crate::events::{EventContent, StateEventContent, StateEventType};
use crate::events::{EventContent, PossiblyRedactedStateEventContent, StateEventType};
/// The content of an `m.policy.rule.server` event.
///
@ -31,6 +31,6 @@ impl EventContent for PossiblyRedactedPolicyRuleServerEventContent {
}
}
impl StateEventContent for PossiblyRedactedPolicyRuleServerEventContent {
impl PossiblyRedactedStateEventContent for PossiblyRedactedPolicyRuleServerEventContent {
type StateKey = String;
}

View File

@ -6,7 +6,7 @@ use ruma_macros::EventContent;
use serde::{Deserialize, Serialize};
use super::{PolicyRuleEventContent, PossiblyRedactedPolicyRuleEventContent};
use crate::events::{EventContent, StateEventContent, StateEventType};
use crate::events::{EventContent, PossiblyRedactedStateEventContent, StateEventType};
/// The content of an `m.policy.rule.user` event.
///
@ -31,6 +31,6 @@ impl EventContent for PossiblyRedactedPolicyRuleUserEventContent {
}
}
impl StateEventContent for PossiblyRedactedPolicyRuleUserEventContent {
impl PossiblyRedactedStateEventContent for PossiblyRedactedPolicyRuleUserEventContent {
type StateKey = String;
}

View File

@ -4,9 +4,7 @@ use ruma_macros::EventContent;
use serde::{Deserialize, Serialize};
use crate::{
events::{
EventContent, RedactContent, RedactedStateEventContent, StateEventContent, StateEventType,
},
events::{EventContent, RedactContent, RedactedStateEventContent, StateEventType},
OwnedRoomAliasId, OwnedServerName, RoomVersionId,
};
@ -83,8 +81,6 @@ impl EventContent for RedactedRoomAliasesEventContent {
}
}
impl StateEventContent for RedactedRoomAliasesEventContent {
impl RedactedStateEventContent for RedactedRoomAliasesEventContent {
type StateKey = OwnedServerName;
}
impl RedactedStateEventContent for RedactedRoomAliasesEventContent {}

View File

@ -10,8 +10,8 @@ use serde::{Deserialize, Serialize};
use crate::{
events::{
AnyStrippedStateEvent, BundledRelations, EventContent, RedactContent,
RedactedStateEventContent, StateEventContent, StateEventType,
AnyStrippedStateEvent, BundledRelations, EventContent, PossiblyRedactedStateEventContent,
RedactContent, RedactedStateEventContent, StateEventType,
},
serde::{CanBeEmpty, Raw, StringEnum},
OwnedMxcUri, OwnedServerName, OwnedServerSigningKeyId, OwnedTransactionId, OwnedUserId,
@ -183,6 +183,10 @@ impl RedactContent for RoomMemberEventContent {
/// This type is used when it's not obvious whether the content is redacted or not.
pub type PossiblyRedactedRoomMemberEventContent = RoomMemberEventContent;
impl PossiblyRedactedStateEventContent for RoomMemberEventContent {
type StateKey = OwnedUserId;
}
/// A member event that has been redacted.
#[derive(Clone, Debug, Deserialize, Serialize)]
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
@ -244,12 +248,10 @@ impl EventContent for RedactedRoomMemberEventContent {
}
}
impl StateEventContent for RedactedRoomMemberEventContent {
impl RedactedStateEventContent for RedactedRoomMemberEventContent {
type StateKey = OwnedUserId;
}
impl RedactedStateEventContent for RedactedRoomMemberEventContent {}
impl RoomMemberEvent {
/// Obtain the membership state, regardless of whether this event is redacted.
pub fn membership(&self) -> &MembershipState {

View File

@ -2,7 +2,8 @@ use js_int::Int;
use serde::Deserialize;
use super::{
relation::BundledRelations, room::redaction::RoomRedactionEventContent, StateEventContent,
relation::BundledRelations, room::redaction::RoomRedactionEventContent,
PossiblyRedactedStateEventContent,
};
use crate::{
serde::CanBeEmpty, MilliSecondsSinceUnixEpoch, OwnedEventId, OwnedTransactionId, OwnedUserId,
@ -51,7 +52,7 @@ impl CanBeEmpty for MessageLikeUnsigned {
/// Extra information about a state event that is not incorporated into the event's hash.
#[derive(Clone, Debug, Deserialize)]
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
pub struct StateUnsigned<C: StateEventContent> {
pub struct StateUnsigned<C: PossiblyRedactedStateEventContent> {
/// The time in milliseconds that has elapsed since the event was sent.
///
/// This field is generated by the local homeserver, and may be incorrect if the local time on
@ -73,14 +74,14 @@ pub struct StateUnsigned<C: StateEventContent> {
pub relations: BundledRelations,
}
impl<C: StateEventContent> StateUnsigned<C> {
impl<C: PossiblyRedactedStateEventContent> StateUnsigned<C> {
/// Create a new `Unsigned` with fields set to `None`.
pub fn new() -> Self {
Self { age: None, transaction_id: None, prev_content: None, relations: Default::default() }
}
}
impl<C: StateEventContent> CanBeEmpty for StateUnsigned<C> {
impl<C: PossiblyRedactedStateEventContent> CanBeEmpty for StateUnsigned<C> {
/// Whether this unsigned data is empty (all fields are `None`).
///
/// This method is used to determine whether to skip serializing the `unsigned` field in room
@ -94,7 +95,7 @@ impl<C: StateEventContent> CanBeEmpty for StateUnsigned<C> {
}
}
impl<C: StateEventContent> Default for StateUnsigned<C> {
impl<C: PossiblyRedactedStateEventContent> Default for StateUnsigned<C> {
fn default() -> Self {
Self::new()
}

View File

@ -1,20 +1,19 @@
use ruma_common::{
events::{StateEventContent, StateUnsigned},
MilliSecondsSinceUnixEpoch, OwnedEventId, OwnedRoomId, OwnedUserId,
events::StaticStateEventContent, MilliSecondsSinceUnixEpoch, OwnedEventId, OwnedRoomId,
OwnedUserId,
};
use ruma_macros::Event;
use serde::de::DeserializeOwned;
/// State event.
#[derive(Clone, Debug, Event)]
pub struct OriginalStateEvent<C: StateEventContent + DeserializeOwned> {
pub struct OriginalStateEvent<C: StaticStateEventContent> {
pub content: C,
pub event_id: OwnedEventId,
pub sender: OwnedUserId,
pub origin_server_ts: MilliSecondsSinceUnixEpoch,
pub room_id: OwnedRoomId,
pub state_key: C::StateKey,
pub unsigned: StateUnsigned<C>,
pub unsigned: C::Unsigned,
}
fn main() {}

View File

@ -1,7 +1,7 @@
//! Implementations of the EventContent derive macro.
#![allow(clippy::too_many_arguments)] // FIXME
use std::borrow::Cow;
use std::{borrow::Cow, fmt};
use proc_macro2::{Span, TokenStream};
use quote::{format_ident, quote, ToTokens};
@ -326,12 +326,11 @@ pub fn expand_event_content(
// We only generate possibly redacted content structs for state events.
let possibly_redacted_event_content = event_kind
.filter(|kind| needs_possibly_redacted(is_custom_possibly_redacted, *kind))
.map(|kind| {
.map(|_| {
generate_possibly_redacted_event_content(
ident,
fields.clone(),
&event_type,
kind,
state_key_type.as_ref(),
unsigned_type.clone(),
&aliases,
@ -350,11 +349,11 @@ pub fn expand_event_content(
fields,
&event_type,
event_kind,
EventKindContentVariation::Original,
state_key_type.as_ref(),
unsigned_type,
&aliases,
ruma_common,
true,
)
.unwrap_or_else(syn::Error::into_compile_error);
let static_event_content_impl =
@ -443,16 +442,14 @@ fn generate_redacted_event_content<'a>(
kept_redacted_fields.iter(),
event_type,
Some(event_kind),
EventKindContentVariation::Redacted,
state_key_type,
unsigned_type,
aliases,
ruma_common,
false,
)
.unwrap_or_else(syn::Error::into_compile_error);
let sub_trait_name = format_ident!("Redacted{event_kind}Content");
let static_event_content_impl =
generate_static_event_content_impl(&redacted_ident, event_type, ruma_common);
@ -480,9 +477,6 @@ fn generate_redacted_event_content<'a>(
#redacted_event_content
#[automatically_derived]
impl #ruma_common::events::#sub_trait_name for #redacted_ident {}
#static_event_content_impl
})
}
@ -491,7 +485,6 @@ fn generate_possibly_redacted_event_content<'a>(
ident: &Ident,
fields: impl Iterator<Item = &'a Field>,
event_type: &LitStr,
event_kind: EventKind,
state_key_type: Option<&TokenStream>,
unsigned_type: Option<TokenStream>,
aliases: &[LitStr],
@ -599,12 +592,12 @@ fn generate_possibly_redacted_event_content<'a>(
&possibly_redacted_ident,
possibly_redacted_fields.iter(),
event_type,
Some(event_kind),
Some(EventKind::State),
EventKindContentVariation::PossiblyRedacted,
state_key_type,
unsigned_type,
aliases,
ruma_common,
false,
)
.unwrap_or_else(syn::Error::into_compile_error);
@ -627,6 +620,11 @@ fn generate_possibly_redacted_event_content<'a>(
Ok(quote! {
#[doc = #doc]
pub type #possibly_redacted_ident = #ident;
#[automatically_derived]
impl #ruma_common::events::PossiblyRedactedStateEventContent for #ident {
type StateKey = #state_key_type;
}
})
}
}
@ -756,16 +754,33 @@ fn generate_event_type_aliases(
Ok(type_aliases)
}
#[derive(PartialEq)]
enum EventKindContentVariation {
Original,
Redacted,
PossiblyRedacted,
}
impl fmt::Display for EventKindContentVariation {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
EventKindContentVariation::Original => Ok(()),
EventKindContentVariation::Redacted => write!(f, "Redacted"),
EventKindContentVariation::PossiblyRedacted => write!(f, "PossiblyRedacted"),
}
}
}
fn generate_event_content_impl<'a>(
ident: &Ident,
mut fields: impl Iterator<Item = &'a Field>,
event_type: &LitStr,
event_kind: Option<EventKind>,
variation: EventKindContentVariation,
state_key_type: Option<&TokenStream>,
unsigned_type: Option<TokenStream>,
aliases: &[LitStr],
ruma_common: &TokenStream,
is_original: bool,
) -> syn::Result<TokenStream> {
let serde = quote! { #ruma_common::exports::serde };
let serde_json = quote! { #ruma_common::exports::serde_json };
@ -845,9 +860,9 @@ fn generate_event_content_impl<'a>(
}
let sub_trait_impl = event_kind.map(|kind| {
let trait_name = format_ident!("{kind}Content");
let trait_name = format_ident!("{variation}{kind}Content");
let state_event_content_impl = (event_kind == Some(EventKind::State)).then(|| {
let state_key = (kind == EventKind::State).then(|| {
assert!(state_key_type.is_some());
quote! {
@ -855,34 +870,32 @@ fn generate_event_content_impl<'a>(
}
});
let original_state_event_content_impl =
(event_kind == Some(EventKind::State) && is_original).then(|| {
let trait_name = format_ident!("Original{kind}Content");
let possibly_redacted_ident = format_ident!("PossiblyRedacted{ident}");
let unsigned_type = unsigned_type.unwrap_or_else(
|| quote! { #ruma_common::events::StateUnsigned<Self::PossiblyRedacted> },
);
quote! {
#[automatically_derived]
impl #ruma_common::events::#trait_name for #ident {
type Unsigned = #unsigned_type;
type PossiblyRedacted = #possibly_redacted_ident;
}
}
});
quote! {
#[automatically_derived]
impl #ruma_common::events::#trait_name for #ident {
#state_event_content_impl
#state_key
}
#original_state_event_content_impl
}
});
let static_state_event_content_impl = (event_kind == Some(EventKind::State)
&& variation == EventKindContentVariation::Original)
.then(|| {
let possibly_redacted_ident = format_ident!("PossiblyRedacted{ident}");
let unsigned_type = unsigned_type.unwrap_or_else(
|| quote! { #ruma_common::events::StateUnsigned<Self::PossiblyRedacted> },
);
quote! {
#[automatically_derived]
impl #ruma_common::events::StaticStateEventContent for #ident {
type PossiblyRedacted = #possibly_redacted_ident;
type Unsigned = #unsigned_type;
}
}
});
let event_types = aliases.iter().chain([event_type]);
let event_content_from_type_impl = type_suffix_data.map(|(_, type_fragment_field)| {
@ -944,8 +957,8 @@ fn generate_event_content_impl<'a>(
}
#event_content_from_type_impl
#sub_trait_impl
#static_state_event_content_impl
})
}