From aa8e48d1c33e9853f8a93f1ebd3853f17f1f4ae8 Mon Sep 17 00:00:00 2001 From: Jonas Platte Date: Mon, 12 Sep 2022 13:10:10 +0200 Subject: [PATCH] events: Allow customizing the unsigned type of state events --- crates/ruma-common/src/events/_custom.rs | 3 +- crates/ruma-common/src/events/content.rs | 7 ++- crates/ruma-common/src/events/kinds.rs | 6 +-- crates/ruma-common/src/events/room/aliases.rs | 4 +- crates/ruma-common/src/events/room/member.rs | 4 +- .../events/ui/03-invalid-event-type.stderr | 2 +- .../ruma-macros/src/events/event_content.rs | 46 ++++++++++++++++++- crates/ruma-macros/src/events/event_enum.rs | 9 +++- 8 files changed, 68 insertions(+), 13 deletions(-) diff --git a/crates/ruma-common/src/events/_custom.rs b/crates/ruma-common/src/events/_custom.rs index 644421af..7d870fff 100644 --- a/crates/ruma-common/src/events/_custom.rs +++ b/crates/ruma-common/src/events/_custom.rs @@ -6,7 +6,7 @@ use super::{ GlobalAccountDataEventType, HasDeserializeFields, MessageLikeEventContent, MessageLikeEventType, RedactContent, RedactedEventContent, RedactedMessageLikeEventContent, RedactedStateEventContent, RoomAccountDataEventContent, RoomAccountDataEventType, - StateEventContent, StateEventType, ToDeviceEventContent, ToDeviceEventType, + StateEventContent, StateEventType, StateUnsigned, ToDeviceEventContent, ToDeviceEventType, }; use crate::RoomVersionId; @@ -80,6 +80,7 @@ impl RedactedMessageLikeEventContent for CustomMessageLikeEventContent {} custom_room_event_content!(CustomStateEventContent, StateEventType); impl StateEventContent for CustomStateEventContent { type StateKey = String; + type Unsigned = StateUnsigned; } impl RedactedStateEventContent for CustomStateEventContent {} diff --git a/crates/ruma-common/src/events/content.rs b/crates/ruma-common/src/events/content.rs index 1d244b0c..79c5ef26 100644 --- a/crates/ruma-common/src/events/content.rs +++ b/crates/ruma-common/src/events/content.rs @@ -3,11 +3,11 @@ use std::fmt; use serde::{de::DeserializeOwned, Serialize}; use serde_json::value::RawValue as RawJsonValue; -use crate::serde::Raw; +use crate::serde::{CanBeEmpty, Raw}; use super::{ EphemeralRoomEventType, GlobalAccountDataEventType, MessageLikeEventType, - RoomAccountDataEventType, StateEventType, ToDeviceEventType, + RoomAccountDataEventType, StateEventType, StateUnsignedFromParts, ToDeviceEventType, }; /// The base trait that all event content types implement. @@ -158,6 +158,9 @@ pub trait RedactedMessageLikeEventContent: MessageLikeEventContent + RedactedEve pub trait StateEventContent: EventContent { /// The type of the event's `state_key` field. type StateKey: AsRef + Clone + fmt::Debug + DeserializeOwned + Serialize; + + /// The type of the event's `unsigned` field. + type Unsigned: Clone + fmt::Debug + Default + CanBeEmpty + StateUnsignedFromParts + Serialize; } /// Content of a non-redacted state event. diff --git a/crates/ruma-common/src/events/kinds.rs b/crates/ruma-common/src/events/kinds.rs index 0352447c..01cc8d13 100644 --- a/crates/ruma-common/src/events/kinds.rs +++ b/crates/ruma-common/src/events/kinds.rs @@ -9,7 +9,7 @@ use super::{ GlobalAccountDataEventContent, MessageLikeEventContent, MessageLikeEventType, MessageLikeUnsigned, Redact, RedactContent, RedactedMessageLikeEventContent, RedactedStateEventContent, RedactedUnsigned, RedactionDeHelper, RoomAccountDataEventContent, - StateEventContent, StateEventType, StateUnsigned, ToDeviceEventContent, + StateEventContent, StateEventType, ToDeviceEventContent, }; use crate::{ serde::from_raw_json_value, EventId, MilliSecondsSinceUnixEpoch, OwnedEventId, OwnedRoomId, @@ -205,7 +205,7 @@ pub struct OriginalStateEvent { pub state_key: C::StateKey, /// Additional key-value pairs not signed by the homeserver. - pub unsigned: StateUnsigned, + pub unsigned: C::Unsigned, } /// An unredacted state event without a `room_id`. @@ -233,7 +233,7 @@ pub struct OriginalSyncStateEvent { pub state_key: C::StateKey, /// Additional key-value pairs not signed by the homeserver. - pub unsigned: StateUnsigned, + pub unsigned: C::Unsigned, } /// A stripped-down state event, used for previews of rooms the user has been invited to. diff --git a/crates/ruma-common/src/events/room/aliases.rs b/crates/ruma-common/src/events/room/aliases.rs index 0c76a39a..a5d2891c 100644 --- a/crates/ruma-common/src/events/room/aliases.rs +++ b/crates/ruma-common/src/events/room/aliases.rs @@ -7,7 +7,7 @@ use serde_json::value::RawValue as RawJsonValue; use crate::{ events::{ EventContent, HasDeserializeFields, RedactContent, RedactedEventContent, - RedactedStateEventContent, StateEventContent, StateEventType, + RedactedStateEventContent, StateEventContent, StateEventType, StateUnsigned, }, OwnedRoomAliasId, OwnedServerName, RoomVersionId, }; @@ -97,6 +97,8 @@ impl EventContent for RedactedRoomAliasesEventContent { impl StateEventContent for RedactedRoomAliasesEventContent { type StateKey = OwnedServerName; + // FIXME: Not actually used + type Unsigned = StateUnsigned; } impl RedactedStateEventContent for RedactedRoomAliasesEventContent {} diff --git a/crates/ruma-common/src/events/room/member.rs b/crates/ruma-common/src/events/room/member.rs index 0c7b9bcd..f7b384f6 100644 --- a/crates/ruma-common/src/events/room/member.rs +++ b/crates/ruma-common/src/events/room/member.rs @@ -11,7 +11,7 @@ use serde_json::value::RawValue as RawJsonValue; use crate::{ events::{ EventContent, HasDeserializeFields, RedactContent, RedactedEventContent, - RedactedStateEventContent, StateEventContent, StateEventType, + RedactedStateEventContent, StateEventContent, StateEventType, StateUnsigned, }, serde::StringEnum, OwnedMxcUri, OwnedServerName, OwnedServerSigningKeyId, OwnedUserId, PrivOwnedStr, @@ -202,6 +202,8 @@ impl EventContent for RedactedRoomMemberEventContent { impl StateEventContent for RedactedRoomMemberEventContent { type StateKey = OwnedUserId; + // FIXME: Not actually used + type Unsigned = StateUnsigned; } impl RedactedStateEventContent for RedactedRoomMemberEventContent {} diff --git a/crates/ruma-common/tests/events/ui/03-invalid-event-type.stderr b/crates/ruma-common/tests/events/ui/03-invalid-event-type.stderr index fd5b9157..2aa3895b 100644 --- a/crates/ruma-common/tests/events/ui/03-invalid-event-type.stderr +++ b/crates/ruma-common/tests/events/ui/03-invalid-event-type.stderr @@ -6,7 +6,7 @@ error: no event type attribute found, add `#[ruma_event(type = "any.room.event", | = note: this error originates in the derive macro `EventContent` (in Nightly builds, run with -Z macro-backtrace for more info) -error: expected one of: `type`, `kind`, `custom_redacted`, `state_key_type`, `alias` +error: expected one of: `type`, `kind`, `custom_redacted`, `state_key_type`, `unsigned_type`, `alias` --> tests/events/ui/03-invalid-event-type.rs:11:14 | 11 | #[ruma_event(event = "m.macro.test", kind = State)] diff --git a/crates/ruma-macros/src/events/event_content.rs b/crates/ruma-macros/src/events/event_content.rs index d2d6be5c..461ebfce 100644 --- a/crates/ruma-macros/src/events/event_content.rs +++ b/crates/ruma-macros/src/events/event_content.rs @@ -1,4 +1,5 @@ //! Implementations of the EventContent derive macro. +#![allow(clippy::too_many_arguments)] // FIXME use std::borrow::Cow; @@ -23,6 +24,8 @@ mod kw { syn::custom_keyword!(type_fragment); // The type to use for a state events' `state_key` field. syn::custom_keyword!(state_key_type); + // The type to use for a state events' `unsigned` field. + syn::custom_keyword!(unsigned_type); // Another type string accepted for deserialization. syn::custom_keyword!(alias); } @@ -42,6 +45,8 @@ enum EventStructMeta { StateKeyType(Box), + UnsignedType(Box), + /// Variant that holds alternate event type accepted for deserialization. Alias(LitStr), } @@ -68,6 +73,13 @@ impl EventStructMeta { } } + fn get_unsigned_type(&self) -> Option<&Type> { + match self { + Self::UnsignedType(ty) => Some(ty), + _ => None, + } + } + fn get_alias(&self) -> Option<&LitStr> { match self { Self::Alias(t) => Some(t), @@ -94,6 +106,10 @@ impl Parse for EventStructMeta { let _: kw::state_key_type = input.parse()?; let _: Token![=] = input.parse()?; input.parse().map(EventStructMeta::StateKeyType) + } else if lookahead.peek(kw::unsigned_type) { + let _: kw::unsigned_type = input.parse()?; + let _: Token![=] = input.parse()?; + input.parse().map(EventStructMeta::UnsignedType) } else if lookahead.peek(kw::alias) { let _: kw::alias = input.parse()?; let _: Token![=] = input.parse()?; @@ -151,6 +167,10 @@ impl MetaAttrs { self.0.iter().find_map(|a| a.get_state_key_type()) } + fn get_unsigned_type(&self) -> Option<&Type> { + self.0.iter().find_map(|a| a.get_unsigned_type()) + } + fn get_aliases(&self) -> impl Iterator { self.0.iter().filter_map(|a| a.get_alias()) } @@ -191,7 +211,7 @@ pub fn expand_event_content( _ => { return Err(syn::Error::new( Span::call_site(), - "multiple event type attribute found, there can only be one", + "multiple event type attributes found, there can only be one", )); } }; @@ -204,7 +224,7 @@ pub fn expand_event_content( _ => { return Err(syn::Error::new( Span::call_site(), - "multiple event kind attribute found, there can only be one", + "multiple event kind attributes found, there can only be one", )); } }; @@ -234,6 +254,19 @@ pub fn expand_event_content( } }; + let unsigned_types: Vec<_> = + content_attr.iter().filter_map(|attrs| attrs.get_unsigned_type()).collect(); + let unsigned_type = match unsigned_types.as_slice() { + [] => None, + [ty] => Some(quote! { #ty }), + _ => { + return Err(syn::Error::new( + Span::call_site(), + "multiple unsigned attributes found, there can only be one", + )); + } + }; + let ident = &input.ident; let fields = match &input.data { syn::Data::Struct(syn::DataStruct { fields, .. }) => fields.iter(), @@ -280,6 +313,7 @@ pub fn expand_event_content( event_type, event_kind, state_key_type.as_ref(), + unsigned_type.clone(), &aliases, ruma_common, ) @@ -292,6 +326,7 @@ pub fn expand_event_content( event_type, event_kind, state_key_type.as_ref(), + unsigned_type, &aliases, ruma_common, ) @@ -317,6 +352,7 @@ fn generate_redacted_event_content<'a>( event_type: &LitStr, event_kind: Option, state_key_type: Option<&TokenStream>, + unsigned_type: Option, aliases: &[&LitStr], ruma_common: &TokenStream, ) -> syn::Result { @@ -402,6 +438,7 @@ fn generate_redacted_event_content<'a>( event_type, event_kind, state_key_type, + unsigned_type, aliases, ruma_common, ) @@ -536,6 +573,7 @@ fn generate_event_content_impl<'a>( event_type: &LitStr, event_kind: Option, state_key_type: Option<&TokenStream>, + unsigned_type: Option, aliases: &[&'a LitStr], ruma_common: &TokenStream, ) -> syn::Result { @@ -621,8 +659,12 @@ fn generate_event_content_impl<'a>( let state_event_content_impl = (event_kind == Some(EventKind::State)).then(|| { assert!(state_key_type.is_some()); + let unsigned_type = unsigned_type + .unwrap_or_else(|| quote! { #ruma_common::events::StateUnsigned }); + quote! { type StateKey = #state_key_type; + type Unsigned = #unsigned_type; } }); diff --git a/crates/ruma-macros/src/events/event_enum.rs b/crates/ruma-macros/src/events/event_enum.rs index 0dae7404..fd51b3e4 100644 --- a/crates/ruma-macros/src/events/event_enum.rs +++ b/crates/ruma-macros/src/events/event_enum.rs @@ -361,8 +361,13 @@ fn expand_content_enum( let variant_arms = variants.iter().map(|v| v.match_arm(quote! { Self })).collect::>(); let sub_trait_name = format_ident!("{kind}Content"); - let state_event_content_impl = - (kind == EventKind::State).then(|| quote! { type StateKey = String; }); + let state_event_content_impl = (kind == EventKind::State).then(|| { + quote! { + type StateKey = String; + // FIXME: Not actually used + type Unsigned = #ruma_common::events::StateUnsigned; + } + }); let from_impl = expand_from_impl(&ident, &content, variants);