Move event content collection trait impl's into macro code

* Move the EventContent and content marker traits into macro.
* Move the raw AnyStateEventContent enum and RawEventContent trait into macro, generate all of the AnyStateEventContent type and impls needed for raw.
* Factor out raw mod codegen to seperate fn, add docs to codegen enum + variants, remove unused imports
This commit is contained in:
Ragotzy.devin 2020-06-02 08:22:15 -04:00 committed by Jonas Platte
parent 5091b9a9a8
commit 80ff10ae6a
No known key found for this signature in database
GPG Key ID: 7D261D771D915378
5 changed files with 149 additions and 129 deletions

View File

@ -10,52 +10,169 @@ use parse::RumaCollectionInput;
pub fn expand_collection(input: RumaCollectionInput) -> syn::Result<TokenStream> {
let attrs = &input.attrs;
let ident = &input.name;
let event_type_str = &input.events;
let variants = input
let variants = input.events.iter().map(to_camel_case).collect::<Vec<_>>();
let content = input
.events
.iter()
.map(|lit| {
let content_docstring = lit;
let var = to_camel_case(lit);
let content = to_event_content(lit);
quote! {
#[doc = #content_docstring]
#var(#content)
}
})
.map(to_event_content_path)
.collect::<Vec<_>>();
let collection = quote! {
#( #attrs )*
#[derive(Clone, Debug, /*Serialize*/)]
//#[serde(untagged)]
#[derive(Clone, Debug, ::serde::Serialize)]
#[serde(untagged)]
#[allow(clippy::large_enum_variant)]
pub enum #ident {
#( #variants ),*
#(
#[doc = #event_type_str]
#variants(#content)
),*
}
};
Ok(collection)
let event_content_impl = quote! {
impl ::ruma_events::EventContent for #ident {
fn event_type(&self) -> &str {
match self {
#( Self::#variants(content) => content.event_type()),*
}
}
}
};
let try_from_raw_impl = quote! {
impl ::ruma_events::TryFromRaw for #ident {
type Raw = raw::#ident;
type Err = String;
fn try_from_raw(raw: Self::Raw) -> Result<Self, Self::Err> {
use raw::#ident::*;
match raw {
#( #variants(c) => {
let content = ::ruma_events::TryFromRaw::try_from_raw(c)
.map_err(|e: <#content as ::ruma_events::TryFromRaw>::Err| e.to_string())?;
// without this ^^^^^^^^^^^ the compiler fails to infer the type
Ok(Self::#variants(content))
}
),*
}
}
}
};
let raw_mod = expand_raw_content_event(&input, &variants)?;
Ok(quote! {
#collection
#try_from_raw_impl
#event_content_impl
impl RoomEventContent for AnyStateEventContent {}
impl StateEventContent for AnyStateEventContent {}
#raw_mod
})
}
/// Splits the given `event_type` string on `.` and `_` removing the `m.` then
/// using only the event name append "EventContent".
fn to_event_content(name: &LitStr) -> Ident {
fn expand_raw_content_event(
input: &RumaCollectionInput,
variants: &[Ident],
) -> syn::Result<TokenStream> {
let ident = &input.name;
let event_type_str = &input.events;
let raw_docs = format!("The raw version of {}, allows for deserialization.", ident);
let raw_content = input
.events
.iter()
.map(to_raw_event_content_path)
.collect::<Vec<_>>();
let raw_collection = quote! {
#[doc = #raw_docs]
#[derive(Clone, Debug)]
#[allow(clippy::large_enum_variant)]
pub enum #ident {
#(
#[doc = #event_type_str]
#variants(#raw_content)
),*
}
};
let raw_event_content_impl = quote! {
impl ::ruma_events::RawEventContent for #ident {
fn from_parts(event_type: &str, input: Box<::serde_json::value::RawValue>) -> Result<Self, String> {
match event_type {
#(
#event_type_str => {
let content = #raw_content::from_parts(event_type, input)?;
Ok(#ident::#variants(content))
},
)*
ev => Err(format!("event not supported {}", ev)),
}
}
}
};
Ok(quote! {
mod raw {
#raw_collection
#raw_event_content_impl
}
})
}
fn to_event_content_path(
name: &LitStr,
) -> syn::punctuated::Punctuated<syn::Token![::], syn::PathSegment> {
let span = name.span();
let name = name.value();
assert_eq!(&name[..2], "m.");
let event = name[2..].split('.').last().unwrap();
let event_str = name[2..].split('.').last().unwrap();
let event = event
let event = event_str
.split('_')
.map(|s| s.chars().next().unwrap().to_uppercase().to_string() + &s[1..])
.collect::<String>();
let content_str = format!("{}EventContent", event);
Ident::new(&content_str, span)
let module = Ident::new(event_str, span);
let content_str = Ident::new(&format!("{}EventContent", event), span);
syn::parse_quote! {
::ruma_events::room::#module::#content_str
}
}
fn to_raw_event_content_path(
name: &LitStr,
) -> syn::punctuated::Punctuated<syn::Token![::], syn::PathSegment> {
let span = name.span();
let name = name.value();
assert_eq!(&name[..2], "m.");
let event_str = name[2..].split('.').last().unwrap();
let event = event_str
.split('_')
.map(|s| s.chars().next().unwrap().to_uppercase().to_string() + &s[1..])
.collect::<String>();
let module = Ident::new(event_str, span);
let content_str = Ident::new(&format!("{}EventContent", event), span);
syn::parse_quote! {
::ruma_events::room::#module::raw::#content_str
}
}
/// Splits the given `event_type` string on `.` and `_` removing the `m.room.` then

View File

@ -14,7 +14,6 @@
clippy::items_after_statements,
clippy::match_same_arms,
clippy::mem_forget,
clippy::missing_docs_in_private_items,
clippy::multiple_inherent_impl,
clippy::mut_mut,
clippy::needless_borrow,

View File

@ -3,12 +3,6 @@
use ruma_events_macros::{FromRaw, StateEventContent};
use ruma_identifiers::RoomAliasId;
use serde::Serialize;
use serde_json::value::RawValue as RawJsonValue;
use crate::{
error::{InvalidEvent, InvalidEventKind},
EventContent, EventJson, RoomEventContent, StateEventContent,
};
/// Informs the room about what room aliases it has been given.
#[derive(Clone, Debug, Serialize, FromRaw, StateEventContent)]

View File

@ -2,13 +2,8 @@
use ruma_events_macros::{FromRaw, StateEventContent};
use serde::Serialize;
use serde_json::value::RawValue as RawJsonValue;
use super::ImageInfo;
use crate::{
error::{InvalidEvent, InvalidEventKind},
EventContent, EventJson, RoomEventContent, StateEventContent,
};
/// A picture that is associated with the room.
///

View File

@ -3,26 +3,17 @@
use std::{
convert::TryFrom,
fmt,
marker::PhantomData,
time::{Duration, SystemTime, UNIX_EPOCH},
time::{SystemTime, UNIX_EPOCH},
};
use js_int::UInt;
use ruma_identifiers::{EventId, RoomId, UserId};
use serde::{
de::{self, Deserialize, Deserializer, Error as _, MapAccess, Visitor},
ser::{Error, SerializeStruct},
Serialize, Serializer,
};
use serde_json::value::RawValue as RawJsonValue;
use crate::{
error::{InvalidEvent, InvalidEventKind},
room::{aliases::AliasesEventContent, avatar::AvatarEventContent},
EventContent, FromRaw, RawEventContent, RoomEventContent, StateEventContent, TryFromRaw,
UnsignedData,
};
use crate::{RawEventContent, RoomEventContent, StateEventContent, TryFromRaw, UnsignedData};
use ruma_events_macros::event_content_collection;
event_content_collection! {
@ -65,50 +56,12 @@ where
pub unsigned: UnsignedData,
}
impl FromRaw for AnyStateEventContent {
type Raw = raw::AnyStateEventContent;
fn from_raw(raw: Self::Raw) -> Self {
use raw::AnyStateEventContent::*;
match raw {
RoomAliases(c) => Self::RoomAliases(FromRaw::from_raw(c)),
RoomAvatar(c) => Self::RoomAvatar(FromRaw::from_raw(c)),
}
}
}
impl Serialize for AnyStateEventContent {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
match self {
AnyStateEventContent::RoomAliases(content) => content.serialize(serializer),
AnyStateEventContent::RoomAvatar(content) => content.serialize(serializer),
}
}
}
impl EventContent for AnyStateEventContent {
fn event_type(&self) -> &str {
match self {
AnyStateEventContent::RoomAliases(content) => content.event_type(),
AnyStateEventContent::RoomAvatar(content) => content.event_type(),
}
}
}
impl RoomEventContent for AnyStateEventContent {}
impl StateEventContent for AnyStateEventContent {}
impl<C> TryFromRaw for StateEvent<C>
where
C: StateEventContent + TryFromRaw,
C::Raw: RawEventContent,
{
type Raw = raw::StateEvent<C::Raw>;
type Raw = raw_state_event::StateEvent<C::Raw>;
type Err = C::Err;
fn try_from_raw(raw: Self::Raw) -> Result<Self, Self::Err> {
@ -156,7 +109,7 @@ where
}
}
mod raw {
mod raw_state_event {
use std::{
fmt,
marker::PhantomData,
@ -164,51 +117,11 @@ mod raw {
};
use js_int::UInt;
use ruma_events_macros::event_content_collection;
use ruma_identifiers::{EventId, RoomId, UserId};
use serde::{
de::{self, Deserialize, Deserializer, Error as _, MapAccess, Visitor},
Serialize,
};
use serde::de::{self, Deserialize, Deserializer, Error as _, MapAccess, Visitor};
use serde_json::value::RawValue as RawJsonValue;
use crate::{
room::{aliases::raw::AliasesEventContent, avatar::raw::AvatarEventContent},
RawEventContent, UnsignedData,
};
event_content_collection! {
/// A state event.
name: AnyStateEventContent,
events: ["m.room.aliases", "m.room.avatar"]
}
impl RawEventContent for AnyStateEventContent {
fn from_parts(event_type: &str, content: Box<RawJsonValue>) -> Result<Self, String> {
fn deserialize_variant<T: RawEventContent>(
ev_type: &str,
input: Box<RawJsonValue>,
variant: fn(T) -> AnyStateEventContent,
) -> Result<AnyStateEventContent, String> {
let content = T::from_parts(ev_type, input)?;
Ok(variant(content))
}
match event_type {
"m.room.avatar" => deserialize_variant::<AvatarEventContent>(
event_type,
content,
AnyStateEventContent::RoomAvatar,
),
"m.room.aliases" => deserialize_variant::<AliasesEventContent>(
event_type,
content,
AnyStateEventContent::RoomAliases,
),
ev => Err(format!("event not supported {}", ev)),
}
}
}
use crate::{RawEventContent, UnsignedData};
/// State event.
#[derive(Clone, Debug)]
@ -403,9 +316,11 @@ mod tests {
use ruma_identifiers::{EventId, RoomAliasId, RoomId, UserId};
use serde_json::{from_value as from_json_value, json, to_value as to_json_value};
use super::{AliasesEventContent, AnyStateEventContent, AvatarEventContent, StateEvent};
use super::{AnyStateEventContent, StateEvent};
use crate::{
room::{ImageInfo, ThumbnailInfo},
room::{
aliases::AliasesEventContent, avatar::AvatarEventContent, ImageInfo, ThumbnailInfo,
},
EventJson, UnsignedData,
};