events: Create EventContentFromType
Implement it for: - event contents that implement Deserialize - event contents with event types with a suffix - custom event contents
This commit is contained in:
parent
9b239663fb
commit
97b212795a
@ -49,6 +49,8 @@ Breaking changes:
|
||||
* Remove the `serde::urlencoded` module
|
||||
* Query string (de)serialization is now done by the `serde_html_form` crate
|
||||
* Rename `RoomEventType` to `TimelineEventType`
|
||||
* Remove `SecretStorageKeyEventContent`'s implementation of `Deserialize`
|
||||
* Use `EventContentFromType::from_parts` instead
|
||||
|
||||
Improvements:
|
||||
|
||||
|
@ -2,9 +2,9 @@ use serde::Serialize;
|
||||
use serde_json::value::RawValue as RawJsonValue;
|
||||
|
||||
use super::{
|
||||
EphemeralRoomEventContent, EphemeralRoomEventType, EventContent, GlobalAccountDataEventContent,
|
||||
GlobalAccountDataEventType, MessageLikeEventContent, MessageLikeEventType,
|
||||
OriginalStateEventContent, RedactContent, RedactedEventContent,
|
||||
EphemeralRoomEventContent, EphemeralRoomEventType, EventContent, EventContentFromType,
|
||||
GlobalAccountDataEventContent, GlobalAccountDataEventType, MessageLikeEventContent,
|
||||
MessageLikeEventType, OriginalStateEventContent, RedactContent, RedactedEventContent,
|
||||
RedactedMessageLikeEventContent, RedactedStateEventContent, RoomAccountDataEventContent,
|
||||
RoomAccountDataEventType, StateEventContent, StateEventType, StateUnsigned,
|
||||
ToDeviceEventContent, ToDeviceEventType,
|
||||
@ -34,6 +34,12 @@ macro_rules! custom_event_content {
|
||||
Ok(Self { event_type: event_type.into() })
|
||||
}
|
||||
}
|
||||
|
||||
impl EventContentFromType for $i {
|
||||
fn from_parts(event_type: &str, _content: &RawJsonValue) -> serde_json::Result<Self> {
|
||||
Ok(Self { event_type: event_type.into() })
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
use std::fmt;
|
||||
|
||||
use serde::{de::DeserializeOwned, Serialize};
|
||||
use serde_json::value::RawValue as RawJsonValue;
|
||||
use serde_json::{from_str as from_json_str, value::RawValue as RawJsonValue};
|
||||
|
||||
use crate::serde::{CanBeEmpty, Raw};
|
||||
|
||||
@ -140,3 +140,19 @@ pub trait RedactedStateEventContent: StateEventContent + RedactedEventContent {}
|
||||
|
||||
/// Content of a to-device event.
|
||||
pub trait ToDeviceEventContent: EventContent<EventType = ToDeviceEventType> {}
|
||||
|
||||
/// Event content that can be deserialized with its event type.
|
||||
pub trait EventContentFromType: EventContent {
|
||||
/// Constructs this event content from the given event type and JSON.
|
||||
#[doc(hidden)]
|
||||
fn from_parts(event_type: &str, content: &RawJsonValue) -> serde_json::Result<Self>;
|
||||
}
|
||||
|
||||
impl<T> EventContentFromType for T
|
||||
where
|
||||
T: EventContent + DeserializeOwned,
|
||||
{
|
||||
fn from_parts(_event_type: &str, content: &RawJsonValue) -> serde_json::Result<Self> {
|
||||
from_json_str(content.get())
|
||||
}
|
||||
}
|
||||
|
@ -46,7 +46,7 @@ fn is_default_bits(val: &UInt) -> bool {
|
||||
|
||||
/// A key description encrypted using a specified algorithm.
|
||||
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, EventContent)]
|
||||
#[derive(Clone, Debug, Serialize, EventContent)]
|
||||
#[ruma_event(type = "m.secret_storage.key.*", kind = GlobalAccountData)]
|
||||
pub struct SecretStorageKeyEventContent {
|
||||
/// The ID of the key.
|
||||
@ -98,10 +98,17 @@ pub enum SecretEncryptionAlgorithm {
|
||||
mod tests {
|
||||
use assert_matches::assert_matches;
|
||||
use js_int::uint;
|
||||
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 as to_raw_json_value,
|
||||
};
|
||||
|
||||
use super::{PassPhrase, SecretEncryptionAlgorithm, SecretStorageKeyEventContent};
|
||||
use crate::{events::GlobalAccountDataEvent, serde::Base64, KeyDerivationAlgorithm};
|
||||
use crate::{
|
||||
events::{EventContentFromType, GlobalAccountDataEvent},
|
||||
serde::Base64,
|
||||
KeyDerivationAlgorithm,
|
||||
};
|
||||
|
||||
#[test]
|
||||
fn test_key_description_serialization() {
|
||||
@ -126,14 +133,19 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_key_description_deserialization() {
|
||||
let json = json!({
|
||||
let json = to_raw_json_value(&json!({
|
||||
"name": "my_key",
|
||||
"algorithm": "m.secret_storage.v1.aes-hmac-sha2",
|
||||
"iv": "YWJjZGVmZ2hpamtsbW5vcA",
|
||||
"mac": "aWRvbnRrbm93d2hhdGFtYWNsb29rc2xpa2U"
|
||||
});
|
||||
}))
|
||||
.unwrap();
|
||||
|
||||
let content = from_json_value::<SecretStorageKeyEventContent>(json).unwrap();
|
||||
let content = <SecretStorageKeyEventContent as EventContentFromType>::from_parts(
|
||||
"m.secret_storage.key.test",
|
||||
&json,
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(content.name.unwrap(), "my_key");
|
||||
assert_matches!(content.passphrase, None);
|
||||
|
||||
@ -150,13 +162,18 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_key_description_deserialization_without_name() {
|
||||
let json = json!({
|
||||
let json = to_raw_json_value(&json!({
|
||||
"algorithm": "m.secret_storage.v1.aes-hmac-sha2",
|
||||
"iv": "YWJjZGVmZ2hpamtsbW5vcA",
|
||||
"mac": "aWRvbnRrbm93d2hhdGFtYWNsb29rc2xpa2U"
|
||||
});
|
||||
}))
|
||||
.unwrap();
|
||||
|
||||
let content = from_json_value::<SecretStorageKeyEventContent>(json).unwrap();
|
||||
let content = <SecretStorageKeyEventContent as EventContentFromType>::from_parts(
|
||||
"m.secret_storage.key.test",
|
||||
&json,
|
||||
)
|
||||
.unwrap();
|
||||
assert!(content.name.is_none());
|
||||
assert_matches!(content.passphrase, None);
|
||||
|
||||
@ -202,7 +219,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_key_description_with_passphrase_deserialization() {
|
||||
let json = json!({
|
||||
let json = to_raw_json_value(&json!({
|
||||
"name": "my_key",
|
||||
"algorithm": "m.secret_storage.v1.aes-hmac-sha2",
|
||||
"iv": "YWJjZGVmZ2hpamtsbW5vcA",
|
||||
@ -213,9 +230,14 @@ mod tests {
|
||||
"iterations": 8,
|
||||
"bits": 256
|
||||
}
|
||||
});
|
||||
}))
|
||||
.unwrap();
|
||||
|
||||
let content = from_json_value::<SecretStorageKeyEventContent>(json).unwrap();
|
||||
let content = <SecretStorageKeyEventContent as EventContentFromType>::from_parts(
|
||||
"m.secret_storage.key.test",
|
||||
&json,
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(content.name.unwrap(), "my_key");
|
||||
|
||||
let passphrase = content.passphrase.unwrap();
|
||||
|
@ -1,7 +1,7 @@
|
||||
use ruma_macros::EventContent;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde::Serialize;
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Serialize, EventContent)]
|
||||
#[derive(Clone, Debug, Serialize, EventContent)]
|
||||
#[ruma_event(type = "m.macro.test.*", kind = GlobalAccountData)]
|
||||
pub struct MacroTestContent {
|
||||
#[ruma_event(type_fragment)]
|
||||
|
@ -894,31 +894,12 @@ fn generate_event_content_impl<'a>(
|
||||
|
||||
let event_types = aliases.iter().chain([event_type]);
|
||||
|
||||
let from_parts_fn_impl = if let Some((_, type_fragment_field)) = &type_suffix_data {
|
||||
let type_prefixes = event_types.map(|ev_type| {
|
||||
ev_type
|
||||
.value()
|
||||
.strip_suffix('*')
|
||||
.expect("aliases have already been checked to have the same suffix")
|
||||
.to_owned()
|
||||
});
|
||||
let type_prefixes = quote! {
|
||||
[#(#type_prefixes,)*]
|
||||
};
|
||||
|
||||
let from_parts_fn_impl = if type_suffix_data.is_some() {
|
||||
quote! {
|
||||
if let Some(type_fragment) = #type_prefixes.iter().find_map(|prefix| ev_type.strip_prefix(prefix)) {
|
||||
let mut content: Self = #serde_json::from_str(content.get())?;
|
||||
content.#type_fragment_field = type_fragment.to_owned();
|
||||
|
||||
::std::result::Result::Ok(content)
|
||||
} else {
|
||||
::std::result::Result::Err(#serde::de::Error::custom(
|
||||
::std::format!("expected event type starting with one of `{:?}`, found `{}`", #type_prefixes, ev_type)
|
||||
))
|
||||
}
|
||||
<Self as #ruma_common::events::EventContentFromType>::from_parts(ev_type, content)
|
||||
}
|
||||
} else {
|
||||
let event_types = event_types.clone();
|
||||
let event_types = quote! {
|
||||
[#(#event_types,)*]
|
||||
};
|
||||
@ -934,6 +915,52 @@ fn generate_event_content_impl<'a>(
|
||||
}
|
||||
};
|
||||
|
||||
let event_content_from_type_impl = type_suffix_data.map(|(_, type_fragment_field)| {
|
||||
let type_prefixes = event_types.map(|ev_type| {
|
||||
ev_type
|
||||
.value()
|
||||
.strip_suffix('*')
|
||||
.expect("aliases have already been checked to have the same suffix")
|
||||
.to_owned()
|
||||
});
|
||||
let type_prefixes = quote! {
|
||||
[#(#type_prefixes,)*]
|
||||
};
|
||||
let fields_without_type_fragment = fields.filter(|f| {
|
||||
!f.attrs.iter().any(|a| {
|
||||
a.path.is_ident("ruma_event") && matches!(a.parse_args(), Ok(EventFieldMeta::TypeFragment))
|
||||
})
|
||||
}).collect::<Vec<_>>();
|
||||
let fields_ident_without_type_fragment = fields_without_type_fragment.iter().filter_map(|f| f.ident.as_ref());
|
||||
|
||||
quote! {
|
||||
impl #ruma_common::events::EventContentFromType for #ident {
|
||||
fn from_parts(
|
||||
ev_type: &::std::primitive::str,
|
||||
content: &#serde_json::value::RawValue,
|
||||
) -> #serde_json::Result<Self> {
|
||||
#[derive(#serde::Deserialize)]
|
||||
struct WithoutTypeFragment {
|
||||
#( #fields_without_type_fragment, )*
|
||||
}
|
||||
|
||||
if let ::std::option::Option::Some(type_fragment) = #type_prefixes.iter().find_map(|prefix| ev_type.strip_prefix(prefix)) {
|
||||
let c: WithoutTypeFragment = #serde_json::from_str(content.get())?;
|
||||
|
||||
::std::result::Result::Ok(Self {
|
||||
#( #fields_ident_without_type_fragment: c.#fields_ident_without_type_fragment, )*
|
||||
#type_fragment_field: type_fragment.to_owned(),
|
||||
})
|
||||
} else {
|
||||
::std::result::Result::Err(#serde::de::Error::custom(
|
||||
::std::format!("expected event type starting with one of `{:?}`, found `{}`", #type_prefixes, ev_type)
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
Ok(quote! {
|
||||
#event_type_ty_decl
|
||||
|
||||
@ -953,6 +980,8 @@ fn generate_event_content_impl<'a>(
|
||||
}
|
||||
}
|
||||
|
||||
#event_content_from_type_impl
|
||||
|
||||
#sub_trait_impl
|
||||
})
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user