Use EventKind/Variation in the Event derive macro

Move the event parsing into it's own mod to be shared by event.rs.
This commit is contained in:
Devin R 2020-07-22 14:22:21 -04:00 committed by Jonas Platte
parent 080a537664
commit 4b9fdcb189
3 changed files with 161 additions and 153 deletions

View File

@ -2,10 +2,9 @@
use proc_macro2::{Span, TokenStream}; use proc_macro2::{Span, TokenStream};
use quote::{format_ident, quote}; use quote::{format_ident, quote};
use syn::{ use syn::{Attribute, Ident, LitStr};
parse::{self, Parse, ParseStream},
Attribute, Expr, ExprLit, Ident, Lit, LitStr, Token, use crate::event_parse::{EventEnumInput, EventKind, EventKindVariation};
};
fn is_non_stripped_room_event(kind: &EventKind, var: &EventKindVariation) -> bool { fn is_non_stripped_room_event(kind: &EventKind, var: &EventKindVariation) -> bool {
matches!(kind, EventKind::Message(_) | EventKind::State(_)) matches!(kind, EventKind::Message(_) | EventKind::State(_))
@ -688,150 +687,3 @@ fn field_return_type(name: &str, var: &EventKindVariation) -> TokenStream {
_ => panic!("the `ruma_events_macros::event_enum::EVENT_FIELD` const was changed"), _ => panic!("the `ruma_events_macros::event_enum::EVENT_FIELD` const was changed"),
} }
} }
/// Custom keywords for the `event_enum!` macro
mod kw {
syn::custom_keyword!(kind);
syn::custom_keyword!(events);
}
// If the variants of this enum change `to_event_path` needs to be updated as well.
#[derive(Eq, PartialEq)]
enum EventKindVariation {
Full,
Sync,
Stripped,
Redacted,
RedactedSync,
RedactedStripped,
}
// If the variants of this enum change `to_event_path` needs to be updated as well.
enum EventKind {
Basic(Ident),
Ephemeral(Ident),
Message(Ident),
State(Ident),
ToDevice(Ident),
}
impl EventKind {
fn is_state(&self) -> bool {
matches!(self, Self::State(_))
}
fn is_message(&self) -> bool {
matches!(self, Self::Message(_))
}
fn to_event_ident(&self, var: &EventKindVariation) -> Option<Ident> {
use EventKindVariation::*;
match (self, var) {
// all `EventKind`s are valid event structs and event enums.
(_, Full) => Some(format_ident!("{}Event", self.get_ident())),
(Self::Ephemeral(i), Sync) | (Self::Message(i), Sync) | (Self::State(i), Sync) => {
Some(format_ident!("Sync{}Event", i))
}
(Self::State(i), Stripped) => Some(format_ident!("Stripped{}Event", i)),
(Self::Message(i), Redacted) | (Self::State(i), Redacted) => {
Some(format_ident!("Redacted{}Event", i))
}
(Self::Message(i), RedactedSync) | (Self::State(i), RedactedSync) => {
Some(format_ident!("RedactedSync{}Event", i))
}
(Self::State(i), RedactedStripped) => Some(format_ident!("RedactedStripped{}Event", i)),
_ => None,
}
}
fn to_event_enum_ident(&self, var: &EventKindVariation) -> Option<Ident> {
Some(format_ident!("Any{}", self.to_event_ident(var)?))
}
/// `Any[kind]EventContent`
fn to_content_enum(&self) -> Ident {
format_ident!("Any{}EventContent", self.get_ident())
}
fn get_ident(&self) -> &Ident {
match self {
EventKind::Basic(i)
| EventKind::Ephemeral(i)
| EventKind::Message(i)
| EventKind::State(i)
| EventKind::ToDevice(i) => i,
}
}
}
impl Parse for EventKind {
fn parse(input: ParseStream) -> syn::Result<Self> {
let ident = input.parse::<Ident>()?;
Ok(match ident.to_string().as_str() {
"Basic" => EventKind::Basic(ident),
"EphemeralRoom" => EventKind::Ephemeral(ident),
"Message" => EventKind::Message(ident),
"State" => EventKind::State(ident),
"ToDevice" => EventKind::ToDevice(ident),
id => {
return Err(syn::Error::new(
input.span(),
format!(
"valid event kinds are Basic, EphemeralRoom, Message, State, ToDevice found `{}`",
id
),
));
}
})
}
}
/// The entire `event_enum!` macro structure directly as it appears in the source code.
pub struct EventEnumInput {
/// Outer attributes on the field, such as a docstring.
attrs: Vec<Attribute>,
/// The name of the event.
name: EventKind,
/// An array of valid matrix event types. This will generate the variants of the event type "name".
/// There needs to be a corresponding variant in `ruma_events::EventType` for
/// this event (converted to a valid Rust-style type name by stripping `m.`, replacing the
/// remaining dots by underscores and then converting from snake_case to CamelCase).
events: Vec<LitStr>,
}
impl Parse for EventEnumInput {
fn parse(input: ParseStream<'_>) -> parse::Result<Self> {
let attrs = input.call(Attribute::parse_outer)?;
// "name" field
input.parse::<kw::kind>()?;
input.parse::<Token![:]>()?;
// the name of our event enum
let name = input.parse::<EventKind>()?;
input.parse::<Token![,]>()?;
// "events" field
input.parse::<kw::events>()?;
input.parse::<Token![:]>()?;
// an array of event names `["m.room.whatever", ...]`
let ev_array = input.parse::<syn::ExprArray>()?;
let events = ev_array
.elems
.into_iter()
.map(|item| {
if let Expr::Lit(ExprLit { lit: Lit::Str(lit_str), .. }) = item {
Ok(lit_str)
} else {
let msg = "values of field `events` are required to be a string literal";
Err(syn::Error::new_spanned(item, msg))
}
})
.collect::<syn::Result<_>>()?;
Ok(Self { attrs, name, events })
}
}

View File

@ -0,0 +1,155 @@
//! Implementation of event enum and event content enum macros.
use matches::matches;
use quote::format_ident;
use syn::{
parse::{self, Parse, ParseStream},
Attribute, Expr, ExprLit, Ident, Lit, LitStr, Token,
};
/// Custom keywords for the `event_enum!` macro
mod kw {
syn::custom_keyword!(kind);
syn::custom_keyword!(events);
}
// If the variants of this enum change `to_event_path` needs to be updated as well.
#[derive(Eq, PartialEq)]
pub enum EventKindVariation {
Full,
Sync,
Stripped,
Redacted,
RedactedSync,
RedactedStripped,
}
// If the variants of this enum change `to_event_path` needs to be updated as well.
pub enum EventKind {
Basic(Ident),
Ephemeral(Ident),
Message(Ident),
State(Ident),
ToDevice(Ident),
}
impl EventKind {
pub fn is_state(&self) -> bool {
matches!(self, Self::State(_))
}
pub fn is_message(&self) -> bool {
matches!(self, Self::Message(_))
}
pub fn to_event_ident(&self, var: &EventKindVariation) -> Option<Ident> {
use EventKindVariation::*;
match (self, var) {
// all `EventKind`s are valid event structs and event enums.
(_, Full) => Some(format_ident!("{}Event", self.get_ident())),
(Self::Ephemeral(i), Sync) | (Self::Message(i), Sync) | (Self::State(i), Sync) => {
Some(format_ident!("Sync{}Event", i))
}
(Self::State(i), Stripped) => Some(format_ident!("Stripped{}Event", i)),
(Self::Message(i), Redacted) | (Self::State(i), Redacted) => {
Some(format_ident!("Redacted{}Event", i))
}
(Self::Message(i), RedactedSync) | (Self::State(i), RedactedSync) => {
Some(format_ident!("RedactedSync{}Event", i))
}
(Self::State(i), RedactedStripped) => Some(format_ident!("RedactedStripped{}Event", i)),
_ => None,
}
}
pub fn to_event_enum_ident(&self, var: &EventKindVariation) -> Option<Ident> {
Some(format_ident!("Any{}", self.to_event_ident(var)?))
}
/// `Any[kind]EventContent`
pub fn to_content_enum(&self) -> Ident {
format_ident!("Any{}EventContent", self.get_ident())
}
pub fn get_ident(&self) -> &Ident {
match self {
EventKind::Basic(i)
| EventKind::Ephemeral(i)
| EventKind::Message(i)
| EventKind::State(i)
| EventKind::ToDevice(i) => i,
}
}
}
impl Parse for EventKind {
fn parse(input: ParseStream) -> syn::Result<Self> {
let ident = input.parse::<Ident>()?;
Ok(match ident.to_string().as_str() {
"Basic" => EventKind::Basic(ident),
"EphemeralRoom" => EventKind::Ephemeral(ident),
"Message" => EventKind::Message(ident),
"State" => EventKind::State(ident),
"ToDevice" => EventKind::ToDevice(ident),
id => {
return Err(syn::Error::new(
input.span(),
format!(
"valid event kinds are Basic, EphemeralRoom, Message, State, ToDevice found `{}`",
id
),
));
}
})
}
}
/// The entire `event_enum!` macro structure directly as it appears in the source code.
pub struct EventEnumInput {
/// Outer attributes on the field, such as a docstring.
pub attrs: Vec<Attribute>,
/// The name of the event.
pub name: EventKind,
/// An array of valid matrix event types. This will generate the variants of the event type "name".
/// There needs to be a corresponding variant in `ruma_events::EventType` for
/// this event (converted to a valid Rust-style type name by stripping `m.`, replacing the
/// remaining dots by underscores and then converting from snake_case to CamelCase).
pub events: Vec<LitStr>,
}
impl Parse for EventEnumInput {
fn parse(input: ParseStream<'_>) -> parse::Result<Self> {
let attrs = input.call(Attribute::parse_outer)?;
// "name" field
input.parse::<kw::kind>()?;
input.parse::<Token![:]>()?;
// the name of our event enum
let name = input.parse::<EventKind>()?;
input.parse::<Token![,]>()?;
// "events" field
input.parse::<kw::events>()?;
input.parse::<Token![:]>()?;
// an array of event names `["m.room.whatever", ...]`
let ev_array = input.parse::<syn::ExprArray>()?;
let events = ev_array
.elems
.into_iter()
.map(|item| {
if let Expr::Lit(ExprLit { lit: Lit::Str(lit_str), .. }) = item {
Ok(lit_str)
} else {
let msg = "values of field `events` are required to be a string literal";
Err(syn::Error::new_spanned(item, msg))
}
})
.collect::<syn::Result<_>>()?;
Ok(Self { attrs, name, events })
}
}

View File

@ -16,13 +16,14 @@ use self::{
expand_basic_event_content, expand_ephemeral_room_event_content, expand_event_content, expand_basic_event_content, expand_ephemeral_room_event_content, expand_event_content,
expand_message_event_content, expand_room_event_content, expand_state_event_content, expand_message_event_content, expand_room_event_content, expand_state_event_content,
}, },
event_enum::{expand_event_enum, EventEnumInput}, event_enum::expand_event_enum,
event_parse::EventEnumInput,
}; };
mod event; mod event;
mod event_content; mod event_content;
mod event_enum; mod event_enum;
mod event_parse;
/// Generates an enum to represent the various Matrix event types. /// Generates an enum to represent the various Matrix event types.
/// ///
/// This macro also implements the necessary traits for the type to serialize and deserialize /// This macro also implements the necessary traits for the type to serialize and deserialize