Add derive macro for StateEventContent

This commit is contained in:
Ragotzy.devin 2020-05-23 10:21:53 -04:00 committed by Jonas Platte
parent 27a9b36499
commit ef6e2e7023
No known key found for this signature in database
GPG Key ID: 7D261D771D915378
6 changed files with 103 additions and 49 deletions

View File

@ -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);
}
}

View File

@ -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()
}

View File

@ -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

View File

@ -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<Self> {
input.parse::<Token![type]>()?;
input.parse::<Token![=]>()?;
Ok(EventMeta::Type(input.parse::<LitStr>()?))
}
}
/// Create a `StateEventContent` implementation for a struct
pub fn expand_state_event(input: DeriveInput) -> syn::Result<TokenStream> {
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::<EventMeta>()?;
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<Self, ::ruma_events::InvalidEvent> {
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 { }
})
}

View File

@ -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<RawJsonValue>) -> Result<Self, InvalidEvent> {
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 {}

View File

@ -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<RawJsonValue>) -> Result<Self, InvalidEvent> {
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 {}