ruma_macros/events/
event_parse.rs

1//! Implementation of event enum and event content enum macros.
2
3use std::fmt;
4
5use proc_macro2::Span;
6use quote::{format_ident, IdentFragment};
7use syn::{
8    braced,
9    parse::{self, Parse, ParseStream},
10    punctuated::Punctuated,
11    Attribute, Ident, LitStr, Path, Token,
12};
13
14/// Custom keywords for the `event_enum!` macro
15mod kw {
16    syn::custom_keyword!(kind);
17    syn::custom_keyword!(events);
18    syn::custom_keyword!(alias);
19    syn::custom_keyword!(ident);
20}
21
22// If the variants of this enum change `to_event_path` needs to be updated as well.
23#[derive(Clone, Copy, Debug, Eq, PartialEq)]
24pub enum EventKindVariation {
25    None,
26    Sync,
27    Original,
28    OriginalSync,
29    Stripped,
30    Initial,
31    Redacted,
32    RedactedSync,
33}
34
35impl fmt::Display for EventKindVariation {
36    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
37        match self {
38            EventKindVariation::None => write!(f, ""),
39            EventKindVariation::Sync => write!(f, "Sync"),
40            EventKindVariation::Original => write!(f, "Original"),
41            EventKindVariation::OriginalSync => write!(f, "OriginalSync"),
42            EventKindVariation::Stripped => write!(f, "Stripped"),
43            EventKindVariation::Initial => write!(f, "Initial"),
44            EventKindVariation::Redacted => write!(f, "Redacted"),
45            EventKindVariation::RedactedSync => write!(f, "RedactedSync"),
46        }
47    }
48}
49
50impl EventKindVariation {
51    pub fn is_redacted(self) -> bool {
52        matches!(self, Self::Redacted | Self::RedactedSync)
53    }
54
55    pub fn is_sync(self) -> bool {
56        matches!(self, Self::OriginalSync | Self::RedactedSync)
57    }
58
59    pub fn to_full(self) -> Self {
60        match self {
61            EventKindVariation::OriginalSync => EventKindVariation::Original,
62            EventKindVariation::RedactedSync => EventKindVariation::Redacted,
63            _ => panic!("No original (unredacted) form of {self:?}"),
64        }
65    }
66}
67
68// If the variants of this enum change `to_event_path` needs to be updated as well.
69#[derive(Clone, Copy, Debug, Eq, PartialEq)]
70pub enum EventKind {
71    GlobalAccountData,
72    RoomAccountData,
73    Ephemeral,
74    MessageLike,
75    State,
76    ToDevice,
77    RoomRedaction,
78    Presence,
79    HierarchySpaceChild,
80    Decrypted,
81}
82
83impl fmt::Display for EventKind {
84    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
85        match self {
86            EventKind::GlobalAccountData => write!(f, "GlobalAccountDataEvent"),
87            EventKind::RoomAccountData => write!(f, "RoomAccountDataEvent"),
88            EventKind::Ephemeral => write!(f, "EphemeralRoomEvent"),
89            EventKind::MessageLike => write!(f, "MessageLikeEvent"),
90            EventKind::State => write!(f, "StateEvent"),
91            EventKind::ToDevice => write!(f, "ToDeviceEvent"),
92            EventKind::RoomRedaction => write!(f, "RoomRedactionEvent"),
93            EventKind::Presence => write!(f, "PresenceEvent"),
94            EventKind::HierarchySpaceChild => write!(f, "HierarchySpaceChildEvent"),
95            EventKind::Decrypted => unreachable!(),
96        }
97    }
98}
99
100impl IdentFragment for EventKind {
101    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
102        fmt::Display::fmt(self, f)
103    }
104}
105
106impl IdentFragment for EventKindVariation {
107    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
108        fmt::Display::fmt(self, f)
109    }
110}
111
112impl EventKind {
113    pub fn is_account_data(self) -> bool {
114        matches!(self, Self::GlobalAccountData | Self::RoomAccountData)
115    }
116
117    pub fn is_timeline(self) -> bool {
118        matches!(self, Self::MessageLike | Self::RoomRedaction | Self::State)
119    }
120
121    pub fn to_event_ident(self, var: EventKindVariation) -> syn::Result<Ident> {
122        use EventKindVariation as V;
123
124        match (self, var) {
125            (_, V::None)
126            | (Self::Ephemeral | Self::MessageLike | Self::State, V::Sync)
127            | (
128                Self::MessageLike | Self::RoomRedaction | Self::State,
129                V::Original | V::OriginalSync | V::Redacted | V::RedactedSync,
130            )
131            | (Self::State, V::Stripped | V::Initial) => Ok(format_ident!("{var}{self}")),
132            _ => Err(syn::Error::new(
133                Span::call_site(),
134                format!("({self:?}, {var:?}) is not a valid event kind / variation combination"),
135            )),
136        }
137    }
138
139    pub fn to_event_enum_ident(self, var: EventKindVariation) -> syn::Result<Ident> {
140        Ok(format_ident!("Any{}", self.to_event_ident(var)?))
141    }
142
143    pub fn to_event_type_enum(self) -> Ident {
144        format_ident!("{}Type", self)
145    }
146
147    /// `Any[kind]EventContent`
148    pub fn to_content_enum(self) -> Ident {
149        format_ident!("Any{}Content", self)
150    }
151
152    /// `AnyFull[kind]EventContent`
153    pub fn to_full_content_enum(self) -> Ident {
154        format_ident!("AnyFull{}Content", self)
155    }
156}
157
158impl Parse for EventKind {
159    fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
160        let ident: Ident = input.parse()?;
161        Ok(match ident.to_string().as_str() {
162            "GlobalAccountData" => EventKind::GlobalAccountData,
163            "RoomAccountData" => EventKind::RoomAccountData,
164            "EphemeralRoom" => EventKind::Ephemeral,
165            "MessageLike" => EventKind::MessageLike,
166            "State" => EventKind::State,
167            "ToDevice" => EventKind::ToDevice,
168            id => {
169                return Err(syn::Error::new_spanned(
170                    ident,
171                    format!(
172                        "valid event kinds are GlobalAccountData, RoomAccountData, EphemeralRoom, \
173                         MessageLike, State, ToDevice; found `{id}`",
174                    ),
175                ));
176            }
177        })
178    }
179}
180
181// This function is only used in the `Event` derive macro expansion code.
182/// Validates the given `ident` is a valid event struct name and returns a tuple of enums
183/// representing the name.
184pub fn to_kind_variation(ident: &Ident) -> Option<(EventKind, EventKindVariation)> {
185    let ident_str = ident.to_string();
186    match ident_str.as_str() {
187        "GlobalAccountDataEvent" => Some((EventKind::GlobalAccountData, EventKindVariation::None)),
188        "RoomAccountDataEvent" => Some((EventKind::RoomAccountData, EventKindVariation::None)),
189        "EphemeralRoomEvent" => Some((EventKind::Ephemeral, EventKindVariation::None)),
190        "SyncEphemeralRoomEvent" => Some((EventKind::Ephemeral, EventKindVariation::Sync)),
191        "OriginalMessageLikeEvent" => Some((EventKind::MessageLike, EventKindVariation::Original)),
192        "OriginalSyncMessageLikeEvent" => {
193            Some((EventKind::MessageLike, EventKindVariation::OriginalSync))
194        }
195        "RedactedMessageLikeEvent" => Some((EventKind::MessageLike, EventKindVariation::Redacted)),
196        "RedactedSyncMessageLikeEvent" => {
197            Some((EventKind::MessageLike, EventKindVariation::RedactedSync))
198        }
199        "OriginalStateEvent" => Some((EventKind::State, EventKindVariation::Original)),
200        "OriginalSyncStateEvent" => Some((EventKind::State, EventKindVariation::OriginalSync)),
201        "StrippedStateEvent" => Some((EventKind::State, EventKindVariation::Stripped)),
202        "InitialStateEvent" => Some((EventKind::State, EventKindVariation::Initial)),
203        "RedactedStateEvent" => Some((EventKind::State, EventKindVariation::Redacted)),
204        "RedactedSyncStateEvent" => Some((EventKind::State, EventKindVariation::RedactedSync)),
205        "ToDeviceEvent" => Some((EventKind::ToDevice, EventKindVariation::None)),
206        "PresenceEvent" => Some((EventKind::Presence, EventKindVariation::None)),
207        "HierarchySpaceChildEvent" => {
208            Some((EventKind::HierarchySpaceChild, EventKindVariation::Stripped))
209        }
210        "OriginalRoomRedactionEvent" => Some((EventKind::RoomRedaction, EventKindVariation::None)),
211        "OriginalSyncRoomRedactionEvent" => {
212            Some((EventKind::RoomRedaction, EventKindVariation::OriginalSync))
213        }
214        "RedactedRoomRedactionEvent" => {
215            Some((EventKind::RoomRedaction, EventKindVariation::Redacted))
216        }
217        "RedactedSyncRoomRedactionEvent" => {
218            Some((EventKind::RoomRedaction, EventKindVariation::RedactedSync))
219        }
220        "DecryptedOlmV1Event" | "DecryptedMegolmV1Event" => {
221            Some((EventKind::Decrypted, EventKindVariation::None))
222        }
223        _ => None,
224    }
225}
226
227pub struct EventEnumEntry {
228    pub attrs: Vec<Attribute>,
229    pub aliases: Vec<LitStr>,
230    pub ev_type: LitStr,
231    pub ev_path: Path,
232    pub ident: Option<Ident>,
233}
234
235impl Parse for EventEnumEntry {
236    fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
237        let (ruma_enum_attrs, attrs) = input
238            .call(Attribute::parse_outer)?
239            .into_iter()
240            .partition::<Vec<_>, _>(|attr| attr.path().is_ident("ruma_enum"));
241        let ev_type: LitStr = input.parse()?;
242        let _: Token![=>] = input.parse()?;
243        let ev_path = input.call(Path::parse_mod_style)?;
244        let has_suffix = ev_type.value().ends_with(".*");
245
246        let mut aliases = Vec::with_capacity(ruma_enum_attrs.len());
247        let mut ident = None;
248
249        for attr_list in ruma_enum_attrs {
250            for attr in attr_list
251                .parse_args_with(Punctuated::<EventEnumAttr, Token![,]>::parse_terminated)?
252            {
253                match attr {
254                    EventEnumAttr::Alias(alias) => {
255                        if alias.value().ends_with(".*") == has_suffix {
256                            aliases.push(alias);
257                        } else {
258                            return Err(syn::Error::new_spanned(
259                                &attr_list,
260                                "aliases should have the same `.*` suffix, or lack thereof, as the main event type",
261                            ));
262                        }
263                    }
264                    EventEnumAttr::Ident(i) => {
265                        if ident.is_some() {
266                            return Err(syn::Error::new_spanned(
267                                &attr_list,
268                                "multiple `ident` attributes found, there can be only one",
269                            ));
270                        }
271
272                        ident = Some(i);
273                    }
274                }
275            }
276        }
277
278        Ok(Self { attrs, aliases, ev_type, ev_path, ident })
279    }
280}
281
282/// The entire `event_enum!` macro structure directly as it appears in the source code.
283pub struct EventEnumDecl {
284    /// Outer attributes on the field, such as a docstring.
285    pub attrs: Vec<Attribute>,
286
287    /// The event kind.
288    pub kind: EventKind,
289
290    /// An array of valid matrix event types.
291    ///
292    /// This will generate the variants of the event type "kind". There needs to be a corresponding
293    /// variant in the `*EventType` enum for this event kind (converted to a valid Rust-style type
294    /// name by stripping `m.`, replacing the remaining dots by underscores and then converting
295    /// from snake_case to CamelCase).
296    pub events: Vec<EventEnumEntry>,
297}
298
299/// The entire `event_enum!` macro structure directly as it appears in the source code.
300pub struct EventEnumInput {
301    pub(crate) enums: Vec<EventEnumDecl>,
302}
303
304impl Parse for EventEnumInput {
305    fn parse(input: ParseStream<'_>) -> parse::Result<Self> {
306        let mut enums = vec![];
307        while !input.is_empty() {
308            let attrs = input.call(Attribute::parse_outer)?;
309
310            let _: Token![enum] = input.parse()?;
311            let kind: EventKind = input.parse()?;
312
313            let content;
314            braced!(content in input);
315            let events = content.parse_terminated(EventEnumEntry::parse, Token![,])?;
316            let events = events.into_iter().collect();
317            enums.push(EventEnumDecl { attrs, kind, events });
318        }
319        Ok(EventEnumInput { enums })
320    }
321}
322
323pub enum EventEnumAttr {
324    Alias(LitStr),
325    Ident(Ident),
326}
327
328impl Parse for EventEnumAttr {
329    fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
330        let lookahead = input.lookahead1();
331
332        if lookahead.peek(kw::alias) {
333            let _: kw::alias = input.parse()?;
334            let _: Token![=] = input.parse()?;
335            let s: LitStr = input.parse()?;
336            Ok(Self::Alias(s))
337        } else if lookahead.peek(kw::ident) {
338            let _: kw::ident = input.parse()?;
339            let _: Token![=] = input.parse()?;
340            let i: Ident = input.parse()?;
341            Ok(Self::Ident(i))
342        } else {
343            Err(lookahead.error())
344        }
345    }
346}