From ef6e2e7023c525ba4229c4c722d7e103d3e0e6c1 Mon Sep 17 00:00:00 2001 From: "Ragotzy.devin" Date: Sat, 23 May 2020 10:21:53 -0400 Subject: [PATCH] Add derive macro for StateEventContent --- ruma-events-macros/src/gen.rs | 14 ++++-- ruma-events-macros/src/lib.rs | 14 +++++- ruma-events-macros/src/parse.rs | 2 +- ruma-events-macros/src/state.rs | 78 +++++++++++++++++++++++++++++++++ src/room/aliases.rs | 22 ---------- src/room/avatar.rs | 22 ---------- 6 files changed, 103 insertions(+), 49 deletions(-) create mode 100644 ruma-events-macros/src/state.rs diff --git a/ruma-events-macros/src/gen.rs b/ruma-events-macros/src/gen.rs index 26c2a57b..477389aa 100644 --- a/ruma-events-macros/src/gen.rs +++ b/ruma-events-macros/src/gen.rs @@ -80,15 +80,25 @@ impl ToTokens for RumaEvent { // let attrs = &self.attrs; let content_name = &self.content_name; // let event_fields = &self.fields; + let event_type = &self.event_type; let name = &self.name; let content_docstring = format!("The payload for `{}`.", name); let content = match &self.content { Content::Struct(fields) => { + // TODO this will all be removed so this is only temp + let event_content_derive = match self.kind { + EventKind::StateEvent => quote! { + #[derive(::ruma_events_macros::StateEventContent)] + #[ruma_event(type = #event_type)] + }, + EventKind::RoomEvent | EventKind::Event => TokenStream::new(), + }; quote! { #[doc = #content_docstring] - #[derive(Clone, Debug, serde::Serialize, ::ruma_events_macros::FromRaw)] + #[derive(Clone, Debug, ::serde::Serialize, ::ruma_events_macros::FromRaw)] + #event_content_derive pub struct #content_name { #(#fields),* } @@ -105,8 +115,6 @@ impl ToTokens for RumaEvent { } }; - // let event_type_name = self.event_type.value(); - content.to_tokens(tokens); } } diff --git a/ruma-events-macros/src/lib.rs b/ruma-events-macros/src/lib.rs index af0f71cf..cc3e950e 100644 --- a/ruma-events-macros/src/lib.rs +++ b/ruma-events-macros/src/lib.rs @@ -36,11 +36,14 @@ use proc_macro::TokenStream; use quote::ToTokens; use syn::{parse_macro_input, DeriveInput}; -use self::{from_raw::expand_from_raw, gen::RumaEvent, parse::RumaEventInput}; +use self::{ + from_raw::expand_from_raw, gen::RumaEvent, parse::RumaEventInput, state::expand_state_event, +}; mod from_raw; mod gen; mod parse; +mod state; // A note about the `example` modules that appears in doctests: // @@ -145,3 +148,12 @@ pub fn derive_from_raw(input: TokenStream) -> TokenStream { .unwrap_or_else(|err| err.to_compile_error()) .into() } + +/// Generates an implementation of `ruma_events::StateEventContent` and it's super traits. +#[proc_macro_derive(StateEventContent, attributes(ruma_event))] +pub fn derive_state_event_content(input: TokenStream) -> TokenStream { + let input = parse_macro_input!(input as DeriveInput); + expand_state_event(input) + .unwrap_or_else(|err| err.to_compile_error()) + .into() +} diff --git a/ruma-events-macros/src/parse.rs b/ruma-events-macros/src/parse.rs index c67bffb4..b91f4094 100644 --- a/ruma-events-macros/src/parse.rs +++ b/ruma-events-macros/src/parse.rs @@ -15,7 +15,7 @@ pub struct RumaEventInput { /// The name of the event. pub name: Ident, - /// The kind of event, determiend by the `kind` field. + /// The kind of event, determined by the `kind` field. pub kind: EventKind, /// The value for the `type` field in the JSON representation of this event. There needs to be a diff --git a/ruma-events-macros/src/state.rs b/ruma-events-macros/src/state.rs new file mode 100644 index 00000000..9680658b --- /dev/null +++ b/ruma-events-macros/src/state.rs @@ -0,0 +1,78 @@ +//! Implementation of the `StateEventContent` derive macro + +use proc_macro2::{Span, TokenStream}; +use quote::quote; +use syn::{ + parse::{Parse, ParseStream}, + DeriveInput, LitStr, Token, +}; + +/// Parses attributes for `*EventContent` derives. +/// +/// `#[ruma_event(type = "m.room.alias")]` +enum EventMeta { + /// Variant holds the "m.room.whatever" event type. + Type(LitStr), +} + +impl Parse for EventMeta { + fn parse(input: ParseStream) -> syn::Result { + input.parse::()?; + input.parse::()?; + Ok(EventMeta::Type(input.parse::()?)) + } +} + +/// Create a `StateEventContent` implementation for a struct +pub fn expand_state_event(input: DeriveInput) -> syn::Result { + let ident = &input.ident; + + let event_type_attr = input + .attrs + .iter() + .find(|attr| attr.path.is_ident("ruma_event")) + .ok_or_else(|| { + let msg = "no event type attribute found, \ + add `#[ruma_events(type = \"any.room.event\")]` \ + below the event content derive"; + + syn::Error::new(Span::call_site(), msg) + })?; + + let event_type = { + let event_meta = event_type_attr.parse_args::()?; + let EventMeta::Type(lit) = event_meta; + lit + }; + + let event_content_impl = quote! { + impl ::ruma_events::EventContent for #ident { + fn event_type(&self) -> &str { + #event_type + } + + fn from_parts( + ev_type: &str, + content: Box<::serde_json::value::RawValue> + ) -> Result { + if ev_type != #event_type { + return Err(::ruma_events::InvalidEvent { + kind: ::ruma_events::error::InvalidEventKind::Deserialization, + message: format!("expected `{}` found {}", #event_type, ev_type), + }); + } + + let ev_json = ::ruma_events::EventJson::from(content); + ev_json.deserialize() + } + } + }; + + Ok(quote! { + #event_content_impl + + impl ::ruma_events::RoomEventContent for #ident { } + + impl ::ruma_events::StateEventContent for #ident { } + }) +} diff --git a/src/room/aliases.rs b/src/room/aliases.rs index ab9d7386..e4eca6c4 100644 --- a/src/room/aliases.rs +++ b/src/room/aliases.rs @@ -20,25 +20,3 @@ ruma_event! { }, } } - -impl EventContent for AliasesEventContent { - fn event_type(&self) -> &str { - "m.room.aliases" - } - - fn from_parts(event_type: &str, content: Box) -> Result { - if event_type != "m.room.aliases" { - return Err(InvalidEvent { - kind: InvalidEventKind::Deserialization, - message: format!("expected `m.room.aliases` found {}", event_type), - }); - } - - let ev_json = EventJson::from(content); - ev_json.deserialize() - } -} - -impl RoomEventContent for AliasesEventContent {} - -impl StateEventContent for AliasesEventContent {} diff --git a/src/room/avatar.rs b/src/room/avatar.rs index dcbe67aa..50227143 100644 --- a/src/room/avatar.rs +++ b/src/room/avatar.rs @@ -27,25 +27,3 @@ ruma_event! { }, } } - -impl EventContent for AvatarEventContent { - fn event_type(&self) -> &str { - "m.room.avatar" - } - - fn from_parts(event_type: &str, content: Box) -> Result { - if event_type != "m.room.avatar" { - return Err(InvalidEvent { - kind: InvalidEventKind::Deserialization, - message: format!("expected `m.room.avatar` found {}", event_type), - }); - } - - let ev_json = EventJson::from(content); - ev_json.deserialize() - } -} - -impl RoomEventContent for AvatarEventContent {} - -impl StateEventContent for AvatarEventContent {}