//! Crate `ruma_events_macros` provides a procedural macro for generating //! [ruma-events](https://github.com/ruma/ruma-events) events. //! //! See the documentation for the `ruma_event!` macro for usage details. #![deny( missing_copy_implementations, missing_debug_implementations, // missing_docs, # Uncomment when https://github.com/rust-lang/rust/pull/60562 is released. )] #![warn( clippy::empty_line_after_outer_attr, clippy::expl_impl_clone_on_copy, clippy::if_not_else, 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, clippy::needless_continue, clippy::single_match_else, clippy::unicode_not_nfc, clippy::use_self, clippy::used_underscore_binding, clippy::wrong_pub_self_convention, clippy::wrong_self_convention )] // Since we support Rust 1.36.0, we can't apply this suggestion yet #![allow(clippy::use_self)] #![recursion_limit = "128"] extern crate proc_macro; use proc_macro::TokenStream; use quote::ToTokens; use syn::{parse_macro_input, DeriveInput}; use self::{ event_content::{expand_message_event, expand_state_event}, from_raw::expand_from_raw, gen::RumaEvent, parse::RumaEventInput, }; mod event_content; mod from_raw; mod gen; mod parse; // A note about the `example` modules that appears in doctests: // // This is necessary because otherwise the expanded code appears in function context, which makes // the compiler interpret the output of the macro as a statement, and proc macros currently aren't // allowed to expand to statements, resulting in a compiler error. /// Generates a Rust type for a Matrix event. /// /// # Examples /// /// The most common form of event is a struct with all the standard fields for an event of its /// kind and a struct for its `content` field: /// /// ```ignore /// # pub mod example { /// # use ruma_events_macros::ruma_event; /// ruma_event! { /// /// Informs the room about what room aliases it has been given. /// AliasesEvent { /// kind: StateEvent, /// event_type: RoomAliases, /// content: { /// /// A list of room aliases. /// pub aliases: Vec, /// } /// } /// } /// # } /// ``` /// /// Occasionally an event will have non-standard fields at its top level (outside the `content` /// field). These extra fields are declared in block labeled with `fields`: /// /// ```ignore /// # pub mod example { /// # use ruma_events_macros::ruma_event; /// ruma_event! { /// /// A redaction of an event. /// RedactionEvent { /// kind: RoomEvent, /// event_type: RoomRedaction, /// fields: { /// /// The ID of the event that was redacted. /// pub redacts: ruma_identifiers::EventId /// }, /// content: { /// /// The reason for the redaction, if any. /// pub reason: Option, /// }, /// } /// } /// # } /// ``` /// /// Sometimes the type of the `content` should be a type alias rather than a struct or enum. This /// is designated with `content_type_alias`: /// /// ```ignore /// # pub mod example { /// # use ruma_events_macros::ruma_event; /// ruma_event! { /// /// Informs the client about the rooms that are considered direct by a user. /// DirectEvent { /// kind: Event, /// event_type: Direct, /// content_type_alias: { /// /// The payload of a `DirectEvent`. /// /// /// /// A mapping of `UserId`'s to a collection of `RoomId`'s which are considered /// /// *direct* for that particular user. /// std::collections::BTreeMap> /// } /// } /// } /// # } /// ``` /// /// If `content` and `content_type_alias` are both supplied, the second one listed will overwrite /// the first. /// /// The event type and content type will have copies generated inside a private `raw` module. These /// "raw" versions are the same, except they implement `serde::Deserialize`. An implementation of /// `FromRaw` will be provided, which will allow the user to deserialize the event type as /// `EventJson`. #[proc_macro] pub fn ruma_event(input: TokenStream) -> TokenStream { let ruma_event_input = syn::parse_macro_input!(input as RumaEventInput); let ruma_event = RumaEvent::from(ruma_event_input); ruma_event.into_token_stream().into() } /// Generates an implementation of `ruma_events::FromRaw`. Only usable inside of `ruma_events`. /// Requires there to be a `raw` module in the same scope, with a type with the same name and fields /// as the one that this macro is used on. #[proc_macro_derive(FromRaw)] pub fn derive_from_raw(input: TokenStream) -> TokenStream { let input = parse_macro_input!(input as DeriveInput); expand_from_raw(input) .unwrap_or_else(|err| err.to_compile_error()) .into() } /// Generates an implementation of `ruma_events::MessageEventContent` and it's super traits. #[proc_macro_derive(MessageEventContent, attributes(ruma_event))] pub fn derive_message_event_content(input: TokenStream) -> TokenStream { let input = parse_macro_input!(input as DeriveInput); expand_message_event(input) .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() }