events: Allow customizing the unsigned type of state events

This commit is contained in:
Jonas Platte 2022-09-12 13:10:10 +02:00
parent 35d8bdd5a1
commit aa8e48d1c3
No known key found for this signature in database
GPG Key ID: AAA7A61F696C3E0C
8 changed files with 68 additions and 13 deletions

View File

@ -6,7 +6,7 @@ use super::{
GlobalAccountDataEventType, HasDeserializeFields, MessageLikeEventContent, GlobalAccountDataEventType, HasDeserializeFields, MessageLikeEventContent,
MessageLikeEventType, RedactContent, RedactedEventContent, RedactedMessageLikeEventContent, MessageLikeEventType, RedactContent, RedactedEventContent, RedactedMessageLikeEventContent,
RedactedStateEventContent, RoomAccountDataEventContent, RoomAccountDataEventType, RedactedStateEventContent, RoomAccountDataEventContent, RoomAccountDataEventType,
StateEventContent, StateEventType, ToDeviceEventContent, ToDeviceEventType, StateEventContent, StateEventType, StateUnsigned, ToDeviceEventContent, ToDeviceEventType,
}; };
use crate::RoomVersionId; use crate::RoomVersionId;
@ -80,6 +80,7 @@ impl RedactedMessageLikeEventContent for CustomMessageLikeEventContent {}
custom_room_event_content!(CustomStateEventContent, StateEventType); custom_room_event_content!(CustomStateEventContent, StateEventType);
impl StateEventContent for CustomStateEventContent { impl StateEventContent for CustomStateEventContent {
type StateKey = String; type StateKey = String;
type Unsigned = StateUnsigned<Self>;
} }
impl RedactedStateEventContent for CustomStateEventContent {} impl RedactedStateEventContent for CustomStateEventContent {}

View File

@ -3,11 +3,11 @@ use std::fmt;
use serde::{de::DeserializeOwned, Serialize}; use serde::{de::DeserializeOwned, Serialize};
use serde_json::value::RawValue as RawJsonValue; use serde_json::value::RawValue as RawJsonValue;
use crate::serde::Raw; use crate::serde::{CanBeEmpty, Raw};
use super::{ use super::{
EphemeralRoomEventType, GlobalAccountDataEventType, MessageLikeEventType, EphemeralRoomEventType, GlobalAccountDataEventType, MessageLikeEventType,
RoomAccountDataEventType, StateEventType, ToDeviceEventType, RoomAccountDataEventType, StateEventType, StateUnsignedFromParts, ToDeviceEventType,
}; };
/// The base trait that all event content types implement. /// The base trait that all event content types implement.
@ -158,6 +158,9 @@ pub trait RedactedMessageLikeEventContent: MessageLikeEventContent + RedactedEve
pub trait StateEventContent: EventContent<EventType = StateEventType> { pub trait StateEventContent: EventContent<EventType = StateEventType> {
/// The type of the event's `state_key` field. /// The type of the event's `state_key` field.
type StateKey: AsRef<str> + Clone + fmt::Debug + DeserializeOwned + Serialize; type StateKey: AsRef<str> + 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. /// Content of a non-redacted state event.

View File

@ -9,7 +9,7 @@ use super::{
GlobalAccountDataEventContent, MessageLikeEventContent, MessageLikeEventType, GlobalAccountDataEventContent, MessageLikeEventContent, MessageLikeEventType,
MessageLikeUnsigned, Redact, RedactContent, RedactedMessageLikeEventContent, MessageLikeUnsigned, Redact, RedactContent, RedactedMessageLikeEventContent,
RedactedStateEventContent, RedactedUnsigned, RedactionDeHelper, RoomAccountDataEventContent, RedactedStateEventContent, RedactedUnsigned, RedactionDeHelper, RoomAccountDataEventContent,
StateEventContent, StateEventType, StateUnsigned, ToDeviceEventContent, StateEventContent, StateEventType, ToDeviceEventContent,
}; };
use crate::{ use crate::{
serde::from_raw_json_value, EventId, MilliSecondsSinceUnixEpoch, OwnedEventId, OwnedRoomId, serde::from_raw_json_value, EventId, MilliSecondsSinceUnixEpoch, OwnedEventId, OwnedRoomId,
@ -205,7 +205,7 @@ pub struct OriginalStateEvent<C: StateEventContent> {
pub state_key: C::StateKey, pub state_key: C::StateKey,
/// Additional key-value pairs not signed by the homeserver. /// Additional key-value pairs not signed by the homeserver.
pub unsigned: StateUnsigned<C>, pub unsigned: C::Unsigned,
} }
/// An unredacted state event without a `room_id`. /// An unredacted state event without a `room_id`.
@ -233,7 +233,7 @@ pub struct OriginalSyncStateEvent<C: StateEventContent> {
pub state_key: C::StateKey, pub state_key: C::StateKey,
/// Additional key-value pairs not signed by the homeserver. /// Additional key-value pairs not signed by the homeserver.
pub unsigned: StateUnsigned<C>, pub unsigned: C::Unsigned,
} }
/// A stripped-down state event, used for previews of rooms the user has been invited to. /// A stripped-down state event, used for previews of rooms the user has been invited to.

View File

@ -7,7 +7,7 @@ use serde_json::value::RawValue as RawJsonValue;
use crate::{ use crate::{
events::{ events::{
EventContent, HasDeserializeFields, RedactContent, RedactedEventContent, EventContent, HasDeserializeFields, RedactContent, RedactedEventContent,
RedactedStateEventContent, StateEventContent, StateEventType, RedactedStateEventContent, StateEventContent, StateEventType, StateUnsigned,
}, },
OwnedRoomAliasId, OwnedServerName, RoomVersionId, OwnedRoomAliasId, OwnedServerName, RoomVersionId,
}; };
@ -97,6 +97,8 @@ impl EventContent for RedactedRoomAliasesEventContent {
impl StateEventContent for RedactedRoomAliasesEventContent { impl StateEventContent for RedactedRoomAliasesEventContent {
type StateKey = OwnedServerName; type StateKey = OwnedServerName;
// FIXME: Not actually used
type Unsigned = StateUnsigned<Self>;
} }
impl RedactedStateEventContent for RedactedRoomAliasesEventContent {} impl RedactedStateEventContent for RedactedRoomAliasesEventContent {}

View File

@ -11,7 +11,7 @@ use serde_json::value::RawValue as RawJsonValue;
use crate::{ use crate::{
events::{ events::{
EventContent, HasDeserializeFields, RedactContent, RedactedEventContent, EventContent, HasDeserializeFields, RedactContent, RedactedEventContent,
RedactedStateEventContent, StateEventContent, StateEventType, RedactedStateEventContent, StateEventContent, StateEventType, StateUnsigned,
}, },
serde::StringEnum, serde::StringEnum,
OwnedMxcUri, OwnedServerName, OwnedServerSigningKeyId, OwnedUserId, PrivOwnedStr, OwnedMxcUri, OwnedServerName, OwnedServerSigningKeyId, OwnedUserId, PrivOwnedStr,
@ -202,6 +202,8 @@ impl EventContent for RedactedRoomMemberEventContent {
impl StateEventContent for RedactedRoomMemberEventContent { impl StateEventContent for RedactedRoomMemberEventContent {
type StateKey = OwnedUserId; type StateKey = OwnedUserId;
// FIXME: Not actually used
type Unsigned = StateUnsigned<Self>;
} }
impl RedactedStateEventContent for RedactedRoomMemberEventContent {} impl RedactedStateEventContent for RedactedRoomMemberEventContent {}

View File

@ -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) = 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 --> tests/events/ui/03-invalid-event-type.rs:11:14
| |
11 | #[ruma_event(event = "m.macro.test", kind = State)] 11 | #[ruma_event(event = "m.macro.test", kind = State)]

View File

@ -1,4 +1,5 @@
//! Implementations of the EventContent derive macro. //! Implementations of the EventContent derive macro.
#![allow(clippy::too_many_arguments)] // FIXME
use std::borrow::Cow; use std::borrow::Cow;
@ -23,6 +24,8 @@ mod kw {
syn::custom_keyword!(type_fragment); syn::custom_keyword!(type_fragment);
// The type to use for a state events' `state_key` field. // The type to use for a state events' `state_key` field.
syn::custom_keyword!(state_key_type); 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. // Another type string accepted for deserialization.
syn::custom_keyword!(alias); syn::custom_keyword!(alias);
} }
@ -42,6 +45,8 @@ enum EventStructMeta {
StateKeyType(Box<Type>), StateKeyType(Box<Type>),
UnsignedType(Box<Type>),
/// Variant that holds alternate event type accepted for deserialization. /// Variant that holds alternate event type accepted for deserialization.
Alias(LitStr), 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> { fn get_alias(&self) -> Option<&LitStr> {
match self { match self {
Self::Alias(t) => Some(t), Self::Alias(t) => Some(t),
@ -94,6 +106,10 @@ impl Parse for EventStructMeta {
let _: kw::state_key_type = input.parse()?; let _: kw::state_key_type = input.parse()?;
let _: Token![=] = input.parse()?; let _: Token![=] = input.parse()?;
input.parse().map(EventStructMeta::StateKeyType) 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) { } else if lookahead.peek(kw::alias) {
let _: kw::alias = input.parse()?; let _: kw::alias = input.parse()?;
let _: Token![=] = input.parse()?; let _: Token![=] = input.parse()?;
@ -151,6 +167,10 @@ impl MetaAttrs {
self.0.iter().find_map(|a| a.get_state_key_type()) 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<Item = &LitStr> { fn get_aliases(&self) -> impl Iterator<Item = &LitStr> {
self.0.iter().filter_map(|a| a.get_alias()) self.0.iter().filter_map(|a| a.get_alias())
} }
@ -191,7 +211,7 @@ pub fn expand_event_content(
_ => { _ => {
return Err(syn::Error::new( return Err(syn::Error::new(
Span::call_site(), 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( return Err(syn::Error::new(
Span::call_site(), 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 ident = &input.ident;
let fields = match &input.data { let fields = match &input.data {
syn::Data::Struct(syn::DataStruct { fields, .. }) => fields.iter(), syn::Data::Struct(syn::DataStruct { fields, .. }) => fields.iter(),
@ -280,6 +313,7 @@ pub fn expand_event_content(
event_type, event_type,
event_kind, event_kind,
state_key_type.as_ref(), state_key_type.as_ref(),
unsigned_type.clone(),
&aliases, &aliases,
ruma_common, ruma_common,
) )
@ -292,6 +326,7 @@ pub fn expand_event_content(
event_type, event_type,
event_kind, event_kind,
state_key_type.as_ref(), state_key_type.as_ref(),
unsigned_type,
&aliases, &aliases,
ruma_common, ruma_common,
) )
@ -317,6 +352,7 @@ fn generate_redacted_event_content<'a>(
event_type: &LitStr, event_type: &LitStr,
event_kind: Option<EventKind>, event_kind: Option<EventKind>,
state_key_type: Option<&TokenStream>, state_key_type: Option<&TokenStream>,
unsigned_type: Option<TokenStream>,
aliases: &[&LitStr], aliases: &[&LitStr],
ruma_common: &TokenStream, ruma_common: &TokenStream,
) -> syn::Result<TokenStream> { ) -> syn::Result<TokenStream> {
@ -402,6 +438,7 @@ fn generate_redacted_event_content<'a>(
event_type, event_type,
event_kind, event_kind,
state_key_type, state_key_type,
unsigned_type,
aliases, aliases,
ruma_common, ruma_common,
) )
@ -536,6 +573,7 @@ fn generate_event_content_impl<'a>(
event_type: &LitStr, event_type: &LitStr,
event_kind: Option<EventKind>, event_kind: Option<EventKind>,
state_key_type: Option<&TokenStream>, state_key_type: Option<&TokenStream>,
unsigned_type: Option<TokenStream>,
aliases: &[&'a LitStr], aliases: &[&'a LitStr],
ruma_common: &TokenStream, ruma_common: &TokenStream,
) -> syn::Result<TokenStream> { ) -> syn::Result<TokenStream> {
@ -621,8 +659,12 @@ fn generate_event_content_impl<'a>(
let state_event_content_impl = (event_kind == Some(EventKind::State)).then(|| { let state_event_content_impl = (event_kind == Some(EventKind::State)).then(|| {
assert!(state_key_type.is_some()); assert!(state_key_type.is_some());
let unsigned_type = unsigned_type
.unwrap_or_else(|| quote! { #ruma_common::events::StateUnsigned<Self> });
quote! { quote! {
type StateKey = #state_key_type; type StateKey = #state_key_type;
type Unsigned = #unsigned_type;
} }
}); });

View File

@ -361,8 +361,13 @@ fn expand_content_enum(
let variant_arms = variants.iter().map(|v| v.match_arm(quote! { Self })).collect::<Vec<_>>(); let variant_arms = variants.iter().map(|v| v.match_arm(quote! { Self })).collect::<Vec<_>>();
let sub_trait_name = format_ident!("{kind}Content"); let sub_trait_name = format_ident!("{kind}Content");
let state_event_content_impl = let state_event_content_impl = (kind == EventKind::State).then(|| {
(kind == EventKind::State).then(|| quote! { type StateKey = String; }); quote! {
type StateKey = String;
// FIXME: Not actually used
type Unsigned = #ruma_common::events::StateUnsigned<Self>;
}
});
let from_impl = expand_from_impl(&ident, &content, variants); let from_impl = expand_from_impl(&ident, &content, variants);