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
|
* Remove the `serde::urlencoded` module
|
||||||
* Query string (de)serialization is now done by the `serde_html_form` crate
|
* Query string (de)serialization is now done by the `serde_html_form` crate
|
||||||
* Rename `RoomEventType` to `TimelineEventType`
|
* Rename `RoomEventType` to `TimelineEventType`
|
||||||
|
* Remove `SecretStorageKeyEventContent`'s implementation of `Deserialize`
|
||||||
|
* Use `EventContentFromType::from_parts` instead
|
||||||
|
|
||||||
Improvements:
|
Improvements:
|
||||||
|
|
||||||
|
@ -2,9 +2,9 @@ use serde::Serialize;
|
|||||||
use serde_json::value::RawValue as RawJsonValue;
|
use serde_json::value::RawValue as RawJsonValue;
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
EphemeralRoomEventContent, EphemeralRoomEventType, EventContent, GlobalAccountDataEventContent,
|
EphemeralRoomEventContent, EphemeralRoomEventType, EventContent, EventContentFromType,
|
||||||
GlobalAccountDataEventType, MessageLikeEventContent, MessageLikeEventType,
|
GlobalAccountDataEventContent, GlobalAccountDataEventType, MessageLikeEventContent,
|
||||||
OriginalStateEventContent, RedactContent, RedactedEventContent,
|
MessageLikeEventType, OriginalStateEventContent, RedactContent, RedactedEventContent,
|
||||||
RedactedMessageLikeEventContent, RedactedStateEventContent, RoomAccountDataEventContent,
|
RedactedMessageLikeEventContent, RedactedStateEventContent, RoomAccountDataEventContent,
|
||||||
RoomAccountDataEventType, StateEventContent, StateEventType, StateUnsigned,
|
RoomAccountDataEventType, StateEventContent, StateEventType, StateUnsigned,
|
||||||
ToDeviceEventContent, ToDeviceEventType,
|
ToDeviceEventContent, ToDeviceEventType,
|
||||||
@ -34,6 +34,12 @@ macro_rules! custom_event_content {
|
|||||||
Ok(Self { event_type: event_type.into() })
|
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 std::fmt;
|
||||||
|
|
||||||
use serde::{de::DeserializeOwned, Serialize};
|
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};
|
use crate::serde::{CanBeEmpty, Raw};
|
||||||
|
|
||||||
@ -140,3 +140,19 @@ pub trait RedactedStateEventContent: StateEventContent + RedactedEventContent {}
|
|||||||
|
|
||||||
/// Content of a to-device event.
|
/// Content of a to-device event.
|
||||||
pub trait ToDeviceEventContent: EventContent<EventType = ToDeviceEventType> {}
|
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.
|
/// A key description encrypted using a specified algorithm.
|
||||||
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
|
#[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)]
|
#[ruma_event(type = "m.secret_storage.key.*", kind = GlobalAccountData)]
|
||||||
pub struct SecretStorageKeyEventContent {
|
pub struct SecretStorageKeyEventContent {
|
||||||
/// The ID of the key.
|
/// The ID of the key.
|
||||||
@ -98,10 +98,17 @@ pub enum SecretEncryptionAlgorithm {
|
|||||||
mod tests {
|
mod tests {
|
||||||
use assert_matches::assert_matches;
|
use assert_matches::assert_matches;
|
||||||
use js_int::uint;
|
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 super::{PassPhrase, SecretEncryptionAlgorithm, SecretStorageKeyEventContent};
|
||||||
use crate::{events::GlobalAccountDataEvent, serde::Base64, KeyDerivationAlgorithm};
|
use crate::{
|
||||||
|
events::{EventContentFromType, GlobalAccountDataEvent},
|
||||||
|
serde::Base64,
|
||||||
|
KeyDerivationAlgorithm,
|
||||||
|
};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_key_description_serialization() {
|
fn test_key_description_serialization() {
|
||||||
@ -126,14 +133,19 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_key_description_deserialization() {
|
fn test_key_description_deserialization() {
|
||||||
let json = json!({
|
let json = to_raw_json_value(&json!({
|
||||||
"name": "my_key",
|
"name": "my_key",
|
||||||
"algorithm": "m.secret_storage.v1.aes-hmac-sha2",
|
"algorithm": "m.secret_storage.v1.aes-hmac-sha2",
|
||||||
"iv": "YWJjZGVmZ2hpamtsbW5vcA",
|
"iv": "YWJjZGVmZ2hpamtsbW5vcA",
|
||||||
"mac": "aWRvbnRrbm93d2hhdGFtYWNsb29rc2xpa2U"
|
"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_eq!(content.name.unwrap(), "my_key");
|
||||||
assert_matches!(content.passphrase, None);
|
assert_matches!(content.passphrase, None);
|
||||||
|
|
||||||
@ -150,13 +162,18 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_key_description_deserialization_without_name() {
|
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",
|
"algorithm": "m.secret_storage.v1.aes-hmac-sha2",
|
||||||
"iv": "YWJjZGVmZ2hpamtsbW5vcA",
|
"iv": "YWJjZGVmZ2hpamtsbW5vcA",
|
||||||
"mac": "aWRvbnRrbm93d2hhdGFtYWNsb29rc2xpa2U"
|
"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!(content.name.is_none());
|
||||||
assert_matches!(content.passphrase, None);
|
assert_matches!(content.passphrase, None);
|
||||||
|
|
||||||
@ -202,7 +219,7 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_key_description_with_passphrase_deserialization() {
|
fn test_key_description_with_passphrase_deserialization() {
|
||||||
let json = json!({
|
let json = to_raw_json_value(&json!({
|
||||||
"name": "my_key",
|
"name": "my_key",
|
||||||
"algorithm": "m.secret_storage.v1.aes-hmac-sha2",
|
"algorithm": "m.secret_storage.v1.aes-hmac-sha2",
|
||||||
"iv": "YWJjZGVmZ2hpamtsbW5vcA",
|
"iv": "YWJjZGVmZ2hpamtsbW5vcA",
|
||||||
@ -213,9 +230,14 @@ mod tests {
|
|||||||
"iterations": 8,
|
"iterations": 8,
|
||||||
"bits": 256
|
"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");
|
assert_eq!(content.name.unwrap(), "my_key");
|
||||||
|
|
||||||
let passphrase = content.passphrase.unwrap();
|
let passphrase = content.passphrase.unwrap();
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
use ruma_macros::EventContent;
|
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)]
|
#[ruma_event(type = "m.macro.test.*", kind = GlobalAccountData)]
|
||||||
pub struct MacroTestContent {
|
pub struct MacroTestContent {
|
||||||
#[ruma_event(type_fragment)]
|
#[ruma_event(type_fragment)]
|
||||||
|
@ -894,31 +894,12 @@ fn generate_event_content_impl<'a>(
|
|||||||
|
|
||||||
let event_types = aliases.iter().chain([event_type]);
|
let event_types = aliases.iter().chain([event_type]);
|
||||||
|
|
||||||
let from_parts_fn_impl = if let Some((_, type_fragment_field)) = &type_suffix_data {
|
let from_parts_fn_impl = if type_suffix_data.is_some() {
|
||||||
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,)*]
|
|
||||||
};
|
|
||||||
|
|
||||||
quote! {
|
quote! {
|
||||||
if let Some(type_fragment) = #type_prefixes.iter().find_map(|prefix| ev_type.strip_prefix(prefix)) {
|
<Self as #ruma_common::events::EventContentFromType>::from_parts(ev_type, content)
|
||||||
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)
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
let event_types = event_types.clone();
|
||||||
let event_types = quote! {
|
let event_types = quote! {
|
||||||
[#(#event_types,)*]
|
[#(#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! {
|
Ok(quote! {
|
||||||
#event_type_ty_decl
|
#event_type_ty_decl
|
||||||
|
|
||||||
@ -953,6 +980,8 @@ fn generate_event_content_impl<'a>(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#event_content_from_type_impl
|
||||||
|
|
||||||
#sub_trait_impl
|
#sub_trait_impl
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user