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,
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<Self>;
}
impl RedactedStateEventContent for CustomStateEventContent {}

View File

@ -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<EventType = StateEventType> {
/// The type of the event's `state_key` field.
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.

View File

@ -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<C: StateEventContent> {
pub state_key: C::StateKey,
/// 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`.
@ -233,7 +233,7 @@ pub struct OriginalSyncStateEvent<C: StateEventContent> {
pub state_key: C::StateKey,
/// 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.

View File

@ -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<Self>;
}
impl RedactedStateEventContent for RedactedRoomAliasesEventContent {}

View File

@ -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<Self>;
}
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)
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)]

View File

@ -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<Type>),
UnsignedType(Box<Type>),
/// 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<Item = &LitStr> {
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<EventKind>,
state_key_type: Option<&TokenStream>,
unsigned_type: Option<TokenStream>,
aliases: &[&LitStr],
ruma_common: &TokenStream,
) -> syn::Result<TokenStream> {
@ -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<EventKind>,
state_key_type: Option<&TokenStream>,
unsigned_type: Option<TokenStream>,
aliases: &[&'a LitStr],
ruma_common: &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(|| {
assert!(state_key_type.is_some());
let unsigned_type = unsigned_type
.unwrap_or_else(|| quote! { #ruma_common::events::StateUnsigned<Self> });
quote! {
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 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<Self>;
}
});
let from_impl = expand_from_impl(&ident, &content, variants);