From d7462442415320a76320da4cf778f3c8f08f3b09 Mon Sep 17 00:00:00 2001 From: Adam <13720823+Frinksy@users.noreply.github.com> Date: Tue, 17 Aug 2021 17:44:33 +0200 Subject: [PATCH] events: Generate From impls for event (content) enums --- crates/ruma-events-macros/src/event_enum.rs | 61 ++++++++++++++++++++- crates/ruma-events-macros/src/lib.rs | 9 +++ crates/ruma-events/CHANGELOG.md | 4 ++ crates/ruma-events/src/enums.rs | 10 ++-- 4 files changed, 78 insertions(+), 6 deletions(-) diff --git a/crates/ruma-events-macros/src/event_enum.rs b/crates/ruma-events-macros/src/event_enum.rs index e0fc0e17..3e029ae8 100644 --- a/crates/ruma-events-macros/src/event_enum.rs +++ b/crates/ruma-events-macros/src/event_enum.rs @@ -2,7 +2,7 @@ use proc_macro2::{Span, TokenStream}; use quote::{format_ident, quote, ToTokens}; -use syn::{Attribute, Ident, LitStr}; +use syn::{Attribute, Data, DataEnum, DeriveInput, Ident, LitStr}; use crate::event_parse::{EventEnumDecl, EventEnumEntry, EventKind, EventKindVariation}; @@ -186,6 +186,8 @@ fn expand_any_with_deser( let redact_impl = expand_redact(&ident, kind, var, variants, ruma_events); + let from_impl = expand_from_impl(ident, &content, variants); + Some(quote! { #any_enum @@ -198,9 +200,34 @@ fn expand_any_with_deser( #event_deserialize_impl #redacted_enum + + #from_impl }) } +fn expand_from_impl( + ty: Ident, + content: &[TokenStream], + variants: &[EventEnumVariant], +) -> TokenStream { + let from_impls = content.iter().zip(variants).map(|(content, variant)| { + let ident = &variant.ident; + let attrs = &variant.attrs; + + quote! { + #[automatically_derived] + #(#attrs)* + impl From<#content> for #ty { + fn from(c: #content) -> Self { + Self::#ident(c) + } + } + } + }); + + quote! { #( #from_impls )* } +} + fn expand_conversion_impl( kind: &EventKind, var: &EventKindVariation, @@ -489,11 +516,14 @@ fn expand_content_enum( } }); + let from_impl = expand_from_impl(ident, &content, variants); + quote! { #content_enum #event_content_impl #marker_trait_impl #redacted_content_enum + #from_impl } } @@ -1037,3 +1067,32 @@ impl EventEnumEntry { Ok(EventEnumVariant { attrs, ident }) } } + +pub(crate) fn expand_from_impls_derived(input: DeriveInput) -> TokenStream { + let variants = match &input.data { + Data::Enum(DataEnum { variants, .. }) => variants, + _ => panic!("this derive macro only works with enums"), + }; + + let from_impls = variants.iter().map(|variant| match &variant.fields { + syn::Fields::Unnamed(fields) if fields.unnamed.len() == 1 => { + let inner_struct = &fields.unnamed.first().unwrap().ty; + let var_ident = &variant.ident; + let id = &input.ident; + quote! { + impl From<#inner_struct> for #id { + fn from(c: #inner_struct) -> Self { + Self::#var_ident(c) + } + } + } + } + _ => { + panic!("this derive macro only works with enum variants with a single unnamed field") + } + }); + + quote! { + #( #from_impls )* + } +} diff --git a/crates/ruma-events-macros/src/lib.rs b/crates/ruma-events-macros/src/lib.rs index 02b7f5d0..44be7f22 100644 --- a/crates/ruma-events-macros/src/lib.rs +++ b/crates/ruma-events-macros/src/lib.rs @@ -15,6 +15,8 @@ use proc_macro_crate::{crate_name, FoundCrate}; use quote::{format_ident, quote}; use syn::{parse_macro_input, DeriveInput}; +use crate::event_enum::expand_from_impls_derived; + use self::{ event::expand_event, event_content::expand_event_content, event_enum::expand_event_enum, event_type::expand_event_type_enum, @@ -107,3 +109,10 @@ pub(crate) fn import_ruma_events() -> pm2::TokenStream { quote! { ::ruma_events } } } + +/// Generates `From` implementations for event enums. +#[proc_macro_derive(EventEnumFromEvent)] +pub fn derive_from_event_to_enum(input: TokenStream) -> TokenStream { + let input = parse_macro_input!(input as DeriveInput); + expand_from_impls_derived(input).into() +} diff --git a/crates/ruma-events/CHANGELOG.md b/crates/ruma-events/CHANGELOG.md index 4de00d07..15297a93 100644 --- a/crates/ruma-events/CHANGELOG.md +++ b/crates/ruma-events/CHANGELOG.md @@ -1,5 +1,9 @@ # [unreleased] +Improvements: + +* Add `From` implementations for event and event content enums + # 0.24.4 Improvements: diff --git a/crates/ruma-events/src/enums.rs b/crates/ruma-events/src/enums.rs index ba9ca26f..8a4d37f1 100644 --- a/crates/ruma-events/src/enums.rs +++ b/crates/ruma-events/src/enums.rs @@ -1,5 +1,5 @@ use ruma_common::MilliSecondsSinceUnixEpoch; -use ruma_events_macros::event_enum; +use ruma_events_macros::{event_enum, EventEnumFromEvent}; use ruma_identifiers::{EventId, RoomId, RoomVersionId, UserId}; use serde::{de, Deserialize, Serialize}; use serde_json::value::RawValue as RawJsonValue; @@ -146,7 +146,7 @@ macro_rules! room_ev_accessor { /// Any room event. #[allow(clippy::large_enum_variant, clippy::exhaustive_enums)] -#[derive(Clone, Debug, Serialize)] +#[derive(Clone, Debug, Serialize, EventEnumFromEvent)] #[serde(untagged)] pub enum AnyRoomEvent { /// Any message event. @@ -171,7 +171,7 @@ impl AnyRoomEvent { /// Any sync room event (room event without a `room_id`, as returned in `/sync` responses) #[allow(clippy::large_enum_variant, clippy::exhaustive_enums)] -#[derive(Clone, Debug, Serialize)] +#[derive(Clone, Debug, Serialize, EventEnumFromEvent)] #[serde(untagged)] pub enum AnySyncRoomEvent { /// Any sync message event @@ -264,7 +264,7 @@ impl<'de> Deserialize<'de> for AnySyncRoomEvent { /// Any redacted room event. #[allow(clippy::large_enum_variant, clippy::exhaustive_enums)] -#[derive(Clone, Debug)] +#[derive(Clone, Debug, EventEnumFromEvent)] pub enum AnyRedactedRoomEvent { /// Any message event that has been redacted. Message(AnyRedactedMessageEvent), @@ -300,7 +300,7 @@ impl From for AnyRoomEvent { /// Any redacted sync room event (room event without a `room_id`, as returned in `/sync` responses) #[allow(clippy::large_enum_variant, clippy::exhaustive_enums)] -#[derive(Clone, Debug)] +#[derive(Clone, Debug, EventEnumFromEvent)] pub enum AnyRedactedSyncRoomEvent { /// Any sync message event that has been redacted. Message(AnyRedactedSyncMessageEvent),