From 80ff10ae6ab30e9347c6b5987d78615505fef049 Mon Sep 17 00:00:00 2001 From: "Ragotzy.devin" Date: Tue, 2 Jun 2020 08:22:15 -0400 Subject: [PATCH] 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 --- ruma-events-macros/src/collection.rs | 161 +++++++++++++++++++++++---- ruma-events-macros/src/lib.rs | 1 - src/room/aliases.rs | 6 - src/room/avatar.rs | 5 - src/state.rs | 105 ++--------------- 5 files changed, 149 insertions(+), 129 deletions(-) diff --git a/ruma-events-macros/src/collection.rs b/ruma-events-macros/src/collection.rs index 05d62a9b..fd608b4a 100644 --- a/ruma-events-macros/src/collection.rs +++ b/ruma-events-macros/src/collection.rs @@ -10,52 +10,169 @@ use parse::RumaCollectionInput; pub fn expand_collection(input: RumaCollectionInput) -> syn::Result { 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::>(); + 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::>(); 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 { + 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 { + 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::>(); + + 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 { + 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 { 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::(); - 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 { + 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::(); + + 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 diff --git a/ruma-events-macros/src/lib.rs b/ruma-events-macros/src/lib.rs index 0e20a11c..581a6eff 100644 --- a/ruma-events-macros/src/lib.rs +++ b/ruma-events-macros/src/lib.rs @@ -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, diff --git a/src/room/aliases.rs b/src/room/aliases.rs index 3762dc29..60fa814a 100644 --- a/src/room/aliases.rs +++ b/src/room/aliases.rs @@ -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)] diff --git a/src/room/avatar.rs b/src/room/avatar.rs index ca87cca8..05cba283 100644 --- a/src/room/avatar.rs +++ b/src/room/avatar.rs @@ -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. /// diff --git a/src/state.rs b/src/state.rs index 2f28f2c7..44b926b0 100644 --- a/src/state.rs +++ b/src/state.rs @@ -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(&self, serializer: S) -> Result - 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 TryFromRaw for StateEvent where C: StateEventContent + TryFromRaw, C::Raw: RawEventContent, { - type Raw = raw::StateEvent; + type Raw = raw_state_event::StateEvent; type Err = C::Err; fn try_from_raw(raw: Self::Raw) -> Result { @@ -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) -> Result { - fn deserialize_variant( - ev_type: &str, - input: Box, - variant: fn(T) -> AnyStateEventContent, - ) -> Result { - let content = T::from_parts(ev_type, input)?; - Ok(variant(content)) - } - - match event_type { - "m.room.avatar" => deserialize_variant::( - event_type, - content, - AnyStateEventContent::RoomAvatar, - ), - "m.room.aliases" => deserialize_variant::( - 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, };