ruma_macros/
lib.rs

1#![doc(html_favicon_url = "https://ruma.dev/favicon.ico")]
2#![doc(html_logo_url = "https://ruma.dev/images/logo.png")]
3//! Procedural macros used by ruma crates.
4//!
5//! See the documentation for the individual macros for usage details.
6
7#![cfg_attr(feature = "__internal_macro_expand", feature(proc_macro_expand))]
8#![warn(missing_docs)]
9#![allow(unreachable_pub)]
10// https://github.com/rust-lang/rust-clippy/issues/9029
11#![allow(clippy::derive_partial_eq_without_eq)]
12
13use identifiers::expand_id_zst;
14use proc_macro::TokenStream;
15use proc_macro2 as pm2;
16use quote::quote;
17use ruma_identifiers_validation::{
18    base64_public_key, event_id, mxc_uri, room_alias_id, room_id, room_version_id, server_name,
19    server_signing_key_version, user_id,
20};
21use syn::{parse_macro_input, DeriveInput, ItemEnum, ItemStruct};
22
23mod api;
24mod events;
25mod identifiers;
26mod serde;
27mod util;
28
29use self::{
30    api::{
31        request::{expand_derive_request, expand_request},
32        response::{expand_derive_response, expand_response},
33    },
34    events::{
35        event::expand_event,
36        event_content::expand_event_content,
37        event_enum::{expand_event_enums, expand_from_impls_derived},
38        event_parse::EventEnumInput,
39        event_type::expand_event_type_enum,
40    },
41    identifiers::IdentifierInput,
42    serde::{
43        as_str_as_ref_str::expand_as_str_as_ref_str,
44        debug_as_ref_str::expand_debug_as_ref_str,
45        deserialize_from_cow_str::expand_deserialize_from_cow_str,
46        display_as_ref_str::expand_display_as_ref_str,
47        enum_as_ref_str::expand_enum_as_ref_str,
48        enum_from_string::expand_enum_from_string,
49        eq_as_ref_str::expand_partial_eq_as_ref_str,
50        ord_as_ref_str::{expand_ord_as_ref_str, expand_partial_ord_as_ref_str},
51        serialize_as_ref_str::expand_serialize_as_ref_str,
52    },
53    util::{import_ruma_common, import_ruma_events},
54};
55
56/// Generates enums to represent the various Matrix event types.
57///
58/// # Generated types
59///
60/// This generates the following enums for each kind:
61///
62/// * `Any{Kind}EventContent`
63/// * `Any{Kind}Event`
64/// * `{Kind}EventType`
65///
66/// It also generates the following enums:
67///
68/// * `AnySync{Kind}Event` for the kinds that have a different format in the `/sync` API:
69///   `EphemeralRoom`, `MessageLike` and `State`.
70/// * `TimelineEventType` which includes the variants from `MessageLikeEventType` and
71///   `StateEventType`
72/// * And extra enums for the `State` kind:
73///     * `AnyInitialStateEvent` for state events sent during room creation.
74///     * `AnyStrippedStateEvent` for state events that are in room state previews when receiving
75///       invites.
76///     * `AnyFullStateEventContent` a helper type to be able to access the `content` and
77///       `prev_content` of a state event.
78///
79/// This macro also implements the following traits for these enums, where it makes sense:
80///
81/// * `Serialize`
82/// * `Deserialize` or `EventContentFromType`
83/// * `{Kind}EventContent`
84/// * Conversion from event type to enum variant, like:
85///     * `From<{event_content_type}> for Any{Kind}EventContent`
86///     * `From<{event_type}> for `Any{Kind}Event`
87///
88/// By default, the enums generated by this macro get a `#[non_exhaustive]` attribute. This
89/// behavior can be controlled by setting the `ruma_unstable_exhaustive_types` compile-time
90/// `cfg` setting as `--cfg=ruma_unstable_exhaustive_types` using `RUSTFLAGS` or
91/// `.cargo/config.toml` (under `[build]` -> `rustflags = ["..."]`). When that setting is
92/// activated, the attribute is not applied so the types are exhaustive.
93///
94/// # Syntax
95///
96/// The basic syntax for using this macro is:
97///
98/// ```ignore
99/// event_enum! {
100///     enum Kind {
101///         "m.first_event_type" => path::to::first_event,
102///         "m.second_event_type" => path::to::second_event,
103///     }
104///
105///     // …
106/// }
107/// ```
108///
109/// ## Enum Kind
110///
111/// The kind must be one of these values, which matches the [`EventContent`] macro:
112///
113/// * `MessageLike` - A message-like event sent in the timeline
114/// * `State` - A state event sent in the timeline
115/// * `GlobalAccountData` - Global config event
116/// * `RoomAccountData` - Per-room config event
117/// * `ToDevice` - Event sent directly to a device
118/// * `EphemeralRoom` - Event that is not persistent in the room
119///
120/// ## Event types
121///
122/// The first part of the event type declaration, before the arrow, is the string matching the
123/// `type` of the event, as defined in the Matrix specification. It must match the `type` attribute
124/// of the [`EventContent`] definition. Account data event types can end with `.*` in case the end
125/// of the event type changes dynamically, see the docs of [`EventContent`] for more details.
126///
127/// This type is used by the enums to know which variant to deserialize according to the `type` that
128/// can be found in the JSON data.
129///
130/// This macro supports deserialization from a second event type string with the `alias` attribute,
131/// which can be useful to support deserializing an event using both its stable and unstable
132/// prefixes, like this:
133///
134/// ```ignore
135/// event_enum! {
136///     enum MessageLike {
137///         #[ruma_enum(alias = "dev.ruma.unstable.foo")]
138///         "m.foo" => path::to::foo,
139///     }
140/// }
141/// ```
142///
143/// By default, this macro tries to generate the event types names from the event type string. It
144/// only recognizes strings that start with the `m.` prefix, which matches stable event types from
145/// the Matrix specification. From there it generates a base name by capitalizing every word,
146/// assuming that words are separated by `.` or `_`. For example, `m.foo.bar` will have the base
147/// name `FooBar`.
148///
149/// If the base name is incorrect, or the event type string uses an unstable prefix, the base name
150/// can be provided with the `ident` attribute, for example:
151///
152/// ```ignore
153/// event_enum! {
154///     enum MessageLike {
155///         #[ruma_enum(ident = FooBar)]
156///         "dev.ruma.foo_bar" => path::to::foo_bar,
157///     }
158/// }
159/// ```
160///
161/// The second part of the event type declaration, after the arrow, is the path of the module where
162/// the event types can be found.
163///
164/// This macro will then assume that all the necessary types are available in the given module to
165/// generate the code for the enums, as if the [`EventContent`] macro was used on a type named
166/// `{base_name}EventContent`.
167///
168/// You can use `cargo doc` to find out more details, its `--document-private-items` flag also lets
169/// you generate documentation for binaries or private parts of a library.
170///
171/// # Example
172///
173/// ```ignore
174/// # // HACK: This is "ignore" because of cyclical dependency drama.
175/// use ruma_macros::event_enum;
176///
177/// event_enum! {
178///     enum ToDevice {
179///         "m.any.event" => super::any_event,
180///         #[ruma_enum(alias = "dev.unstable.prefix.other.event")]
181///         "m.other.event" => super::other_event,
182///         #[cfg(feature = "unstable-mscXXXX")]
183///         #[ruma_enum(ident = NewEventEventContent)]
184///         "org.matrix.mscXXXX.new_event" => super::new_event,
185///     }
186///
187///     enum State {
188///         "m.more.events" => super::more_events,
189///         "m.different.event" => super::different_event,
190///     }
191/// }
192/// ```
193#[proc_macro]
194pub fn event_enum(input: TokenStream) -> TokenStream {
195    let event_enum_input = syn::parse_macro_input!(input as EventEnumInput);
196
197    let ruma_common = import_ruma_common();
198
199    let enums = event_enum_input
200        .enums
201        .iter()
202        .map(|e| expand_event_enums(e).unwrap_or_else(syn::Error::into_compile_error))
203        .collect::<pm2::TokenStream>();
204
205    let event_types = expand_event_type_enum(event_enum_input, ruma_common)
206        .unwrap_or_else(syn::Error::into_compile_error);
207
208    let tokens = quote! {
209        #enums
210        #event_types
211    };
212
213    tokens.into()
214}
215
216/// Generates traits implementations and types for an event content.
217///
218/// # Trait implementations
219///
220/// This macro implements the following traits for the type on which it is applied:
221///
222/// * `{kind}EventContent`
223/// * `StaticEventContent`
224/// * `StaticStateEventContent`, for the `State` kind.
225///
226/// # Generated types
227///
228/// It also generates type aliases and modified clones depending on the kind of event. To generate
229/// the base name of those types, the macro simply removes `Content` from the name of the type,
230/// which means that to apply this macro to a type, its name must always end with `Content`. And for
231/// compatibility with the [`event_enum!`] macro, the name should actually end with `EventContent`.
232///
233/// Some kinds can generate a modified clone of the event content type. For instance, for an event
234/// content type named `FooEventContent`:
235///
236/// * `RedactedFooEventContent`: the redacted form of the event content, for the `MessageLike` and
237///   `State` kinds. It also generates the `RedactContent` implementation which applies the
238///   redaction algorithm according to the Matrix specification.
239///
240///   The generated type implements `Redacted{Kind}EventContent`, `StaticEventContent`, `Serialize`
241///   and `Deserialize`.
242///
243///   The generation only works if the type is a struct with named fields. To keep a field after
244///   redaction, the `#[ruma_event(skip_redaction)]` attribute can be applied to that field.
245///
246///   To skip the generation of this type and trait to implement a custom redaction, or because it
247///   is not a struct with named fields, the `#[ruma_event(custom_redacted)]` attribute can be used
248///   on the container. The `RedactedFooEventContent` type must still exist and implement the same
249///   traits, even if it is only a type alias, and the `RedactContent` trait must still be
250///   implemented for those kinds.
251/// * `PossiblyRedactedFooEventContent`: the form of the event content that is used when we don't
252///   know whether a `State` event is redacted or not. It means that on this type any field that is
253///   redacted must be optional, or it must have the `#[serde(default)]` attribute for
254///   deserialization.
255///
256///   The generated type implements `PossiblyRedactedStateEventContent`, `StaticEventContent`,
257///   `Serialize` and `Deserialize`.
258///
259///   The generation uses the rules as the redacted type, using the `#[ruma_event(skip_redaction)]`
260///   attribute.
261///
262///   To skip the generation of this type to use a custom type, the
263///   `#[ruma_event(custom_possibly_redacted)]` attribute can be used on the container. The
264///   `PossiblyRedactedFooEventContent` type must still exist for the `State` kind and implement the
265///   same traits, even if it is only a type alias.
266///
267/// Event content types of the `MessageLike` kind that use the `Relation` type also need a clone of
268/// the event content without the `relates_to` field for use within relations, where nested
269/// relations are not meant to be serialized by homeservers. This macro can generate a
270/// `FooEventContentWithoutRelation` type if the `#[ruma_event(without_relation)]` attribute is
271/// applied on the container. It also generates `From<FooEventContent> for
272/// FooEventContentWithoutRelation` and `FooEventContentWithoutRelation::with_relation()`.
273///
274/// By default, the generated types get a `#[non_exhaustive]` attribute. This behavior can be
275/// controlled by setting the `ruma_unstable_exhaustive_types` compile-time `cfg` setting as
276/// `--cfg=ruma_unstable_exhaustive_types` using `RUSTFLAGS` or `.cargo/config.toml` (under
277/// `[build]` -> `rustflags = ["..."]`). When that setting is activated, the attribute is not
278/// applied so the types are exhaustive.
279///
280/// # Type aliases
281///
282/// All kinds generate at least one type alias for the full event format. For the same example type
283/// named `FooEventContent`, the first type alias generated is `type FooEvent =
284/// {Kind}Event<FooEventContent>`.
285///
286/// The only exception for this is if the type has the `GlobalAccountData + RoomAccountData` kinds,
287/// it generates two type aliases with prefixes:
288///
289/// * `type GlobalFooEvent = GlobalAccountDataEvent<FooEventContent>`
290/// * `type RoomFooEvent = RoomAccountDataEvent<FooEventContent>`
291///
292/// Some kinds generate more type aliases:
293///
294/// * `type SyncFooEvent = Sync{Kind}Event<FooEventContent>`: an event received via the `/sync` API,
295///   for the `MessageLike`, `State` and `EphemeralRoom` kinds
296/// * `type OriginalFooEvent = Original{Kind}Event<FooEventContent>`, a non-redacted event, for the
297///   `MessageLike` and `State` kinds
298/// * `type OriginalSyncFooEvent = OriginalSync{Kind}Event<FooEventContent>`, a non-redacted event
299///   received via the `/sync` API, for the `MessageLike` and `State` kinds
300/// * `type RedactedFooEvent = Redacted{Kind}Event<RedactedFooEventContent>`, a redacted event, for
301///   the `MessageLike` and `State` kinds
302/// * `type OriginalSyncFooEvent = RedactedSync{Kind}Event<RedactedFooEventContent>`, a redacted
303///   event received via the `/sync` API, for the `MessageLike` and `State` kinds
304/// * `type InitialFooEvent = InitialStateEvent<FooEventContent>`, an event sent during room
305///   creation, for the `State` kind
306/// * `type StrippedFooEvent = StrippedStateEvent<PossiblyRedactedFooEventContent>`, an event that
307///   is in a room state preview when receiving an invite, for the `State` kind
308///
309/// You can use `cargo doc` to find out more details, its `--document-private-items` flag also lets
310/// you generate documentation for binaries or private parts of a library.
311///
312/// # Syntax
313///
314/// The basic syntax for using this macro is:
315///
316/// ```ignore
317/// #[derive(Clone, Debug, Deserialize, Serialize, EventContent)]
318/// #[ruma_event(type = "m.foo_bar", kind = MessageLike)]
319/// pub struct FooBarEventContent {
320///     data: String,
321/// }
322/// ```
323///
324/// ## Container attributes
325///
326/// The following settings can be used on the container, with the `#[ruma_event(_)]` attribute.
327/// `type` and `kind` are always required.
328///
329/// ### `type = "m.event_type"`
330///
331/// The `type` of the event according to the Matrix specification, always required. This is usually
332/// a string with an `m.` prefix.
333///
334/// Types with an account data kind can also use the `.*` suffix, if the end of the type changes
335/// dynamically. It must be associated with a field that has the `#[ruma_event(type_fragment)]`
336/// attribute that will store the end of the event type.
337///
338/// ### `kind = Kind`
339///
340/// The kind of the event, always required. It must be one of these values, which matches the
341/// [`event_enum!`] macro:
342///
343/// * `MessageLike` - A message-like event sent in the timeline
344/// * `State` - A state event sent in the timeline
345/// * `GlobalAccountData` - Global config event
346/// * `RoomAccountData` - Per-room config event
347/// * `ToDevice` - Event sent directly to a device
348/// * `EphemeralRoom` - Event that is not persistent in the room
349///
350/// It is possible to implement both account data kinds for the same type by using the syntax `kind
351/// = GlobalAccountData + RoomAccountData`.
352///
353/// ### `alias = "m.event_type"`
354///
355/// An alternate `type` for the event, used during deserialization. It is usually used for
356/// deserializing an event type using both its stable and unstable prefix.
357///
358/// ### `state_key = StringType`
359///
360/// The type of the state key of the event, required and only supported if the kind is `State`. This
361/// type should be a string type like `String`, `EmptyStateKey` or an identifier type generated with
362/// the `IdZst` macro.
363///
364/// ### `unsigned_type = UnsignedType`
365///
366/// A custom type to use for the `Unsigned` type of the `StaticStateEventContent` implementation if
367/// the kind is `State`. Only necessary if the `StateUnsigned` type is not appropriate for this
368/// type.
369///
370/// ### `custom_redacted`
371///
372/// If the kind requires a `Redacted{}EventContent` type and a `RedactContent` implementation and it
373/// is not possible to generate them with the macro, setting this attribute prevents the macro from
374/// trying to generate them. The type and trait must be implemented manually.
375///
376/// ### `custom_possibly_redacted`
377///
378/// If the kind requires a `PossiblyRedacted{}EventContent` type and it is not possible to generate
379/// it with the macro, setting this attribute prevents the macro from trying to generate it. The
380/// type must be implemented manually.
381///
382/// ### `without_relation`
383///
384/// If this is set, the macro will try to generate an `{}EventContentWithoutRelation` which is a
385/// clone of the current type with the `relates_to` field removed.
386///
387/// ## Field attributes
388///
389/// The following settings can be used on the fields of a struct, with the `#[ruma_event(_)]`
390/// attribute.
391///
392/// ### `skip_redaction`
393///
394/// If a `Redacted{}EventContent` type is generated by the macro, this field will be kept after
395/// redaction.
396///
397/// ### `type_fragment`
398///
399/// If the event content's kind is account data and its type ends with the `.*`, this field is
400/// required and will store the end of the event's type.
401///
402/// # Example
403///
404/// An example can be found in the docs at the root of `ruma_events`.
405#[proc_macro_derive(EventContent, attributes(ruma_event))]
406pub fn derive_event_content(input: TokenStream) -> TokenStream {
407    let ruma_events = import_ruma_events();
408    let input = parse_macro_input!(input as DeriveInput);
409
410    expand_event_content(&input, &ruma_events).unwrap_or_else(syn::Error::into_compile_error).into()
411}
412
413/// Generates trait implementations for Matrix event types.
414///
415/// This macro parses the name of the type on which it is applied to decide what to do, which means
416/// that it only works on a fixed list of types. It also requires the type to be a struct with named
417/// fields, with one of these fields named `content`.
418///
419/// This macro implements at least `Deserialize` for the type on which it is applied.
420///
421/// If the type is an `OriginalSync` or `RedactedSync` event, this implements conversion
422/// helpers to the non-sync version of the event type. For example if the event type is
423/// `OriginalSyncMessageLikeEvent`, this will generate `From<OriginalMessageLikeEvent> for
424/// OriginalSyncMessageLikeEvent` and `OriginalSyncMessageLikeEvent::into_full_event()`.
425///
426/// If the type is a non-stripped timeline event, i.e. a struct with an `event_id` field, this
427/// implements `PartialEq`, `Eq`, `PartialOrd` and `Ord` by comparing the `event_id` fields.
428///
429/// You can use `cargo doc` to find out more details, its `--document-private-items` flag also lets
430/// you generate documentation for binaries or private parts of a library.
431#[proc_macro_derive(Event, attributes(ruma_event))]
432pub fn derive_event(input: TokenStream) -> TokenStream {
433    let input = parse_macro_input!(input as DeriveInput);
434    expand_event(input).unwrap_or_else(syn::Error::into_compile_error).into()
435}
436
437/// Generates `From` implementations for an enum for all its variants.
438#[proc_macro_derive(EventEnumFromEvent)]
439pub fn derive_from_event_to_enum(input: TokenStream) -> TokenStream {
440    let input = parse_macro_input!(input as DeriveInput);
441    expand_from_impls_derived(input).into()
442}
443
444/// Generate methods and trait impl's for ZST identifier type.
445///
446/// This macro generates an `Owned*` wrapper type for the identifier type. This wrapper type is
447/// variable, by default it'll use [`Box`], but it can be changed at compile time
448/// by setting `--cfg=ruma_identifiers_storage=...` using `RUSTFLAGS` or `.cargo/config.toml` (under
449/// `[build]` -> `rustflags = ["..."]`). Currently the only supported value is `Arc`, that uses
450/// [`Arc`](std::sync::Arc) as a wrapper type.
451///
452/// This macro implements:
453///
454/// * Conversions to and from string types, `AsRef<[u8]>` and `AsRef<str>`, as well as `as_str()`
455///   and `as_bytes()` methods. The borrowed type can be converted from a borrowed string without
456///   allocation.
457/// * Conversions to and from borrowed and owned type.
458/// * `Deref`, `AsRef` and `Borrow` to the borrowed type for the owned type.
459/// * `PartialEq` implementations for testing equality with string types and owned and borrowed
460///   types.
461///
462/// # Attributes
463///
464/// * `#[ruma_api(validate = PATH)]`: the path to a function to validate the string during parsing
465///   and deserialization. By default, the types implement `From` string types, when this is set
466///   they implement `TryFrom`.
467///
468/// # Examples
469///
470/// ```ignore
471/// # // HACK: This is "ignore" because of cyclical dependency drama.
472/// use ruma_macros::IdZst;
473///
474/// #[derive(PartialEq, Eq, PartialOrd, Ord, Hash, IdZst)]
475/// #[ruma_id(validate = ruma_identifiers_validation::user_id::validate)]
476/// pub struct UserId(str);
477/// ```
478#[proc_macro_derive(IdZst, attributes(ruma_id))]
479pub fn derive_id_zst(input: TokenStream) -> TokenStream {
480    let input = parse_macro_input!(input as ItemStruct);
481    expand_id_zst(input).unwrap_or_else(syn::Error::into_compile_error).into()
482}
483
484/// Compile-time checked `EventId` construction.
485#[proc_macro]
486pub fn event_id(input: TokenStream) -> TokenStream {
487    let IdentifierInput { dollar_crate, id } = parse_macro_input!(input as IdentifierInput);
488    assert!(event_id::validate(&id.value()).is_ok(), "Invalid event id");
489
490    let output = quote! {
491        <&#dollar_crate::EventId as ::std::convert::TryFrom<&str>>::try_from(#id).unwrap()
492    };
493
494    output.into()
495}
496
497/// Compile-time checked `RoomAliasId` construction.
498#[proc_macro]
499pub fn room_alias_id(input: TokenStream) -> TokenStream {
500    let IdentifierInput { dollar_crate, id } = parse_macro_input!(input as IdentifierInput);
501    assert!(room_alias_id::validate(&id.value()).is_ok(), "Invalid room_alias_id");
502
503    let output = quote! {
504        <&#dollar_crate::RoomAliasId as ::std::convert::TryFrom<&str>>::try_from(#id).unwrap()
505    };
506
507    output.into()
508}
509
510/// Compile-time checked `RoomId` construction.
511#[proc_macro]
512pub fn room_id(input: TokenStream) -> TokenStream {
513    let IdentifierInput { dollar_crate, id } = parse_macro_input!(input as IdentifierInput);
514    assert!(room_id::validate(&id.value()).is_ok(), "Invalid room_id");
515
516    let output = quote! {
517        <&#dollar_crate::RoomId as ::std::convert::TryFrom<&str>>::try_from(#id).unwrap()
518    };
519
520    output.into()
521}
522
523/// Compile-time checked `RoomVersionId` construction.
524#[proc_macro]
525pub fn room_version_id(input: TokenStream) -> TokenStream {
526    let IdentifierInput { dollar_crate, id } = parse_macro_input!(input as IdentifierInput);
527    assert!(room_version_id::validate(&id.value()).is_ok(), "Invalid room_version_id");
528
529    let output = quote! {
530        <#dollar_crate::RoomVersionId as ::std::convert::TryFrom<&str>>::try_from(#id).unwrap()
531    };
532
533    output.into()
534}
535
536/// Compile-time checked `ServerSigningKeyVersion` construction.
537#[proc_macro]
538pub fn server_signing_key_version(input: TokenStream) -> TokenStream {
539    let IdentifierInput { dollar_crate, id } = parse_macro_input!(input as IdentifierInput);
540    assert!(
541        server_signing_key_version::validate(&id.value()).is_ok(),
542        "Invalid server_signing_key_version"
543    );
544
545    let output = quote! {
546        <&#dollar_crate::ServerSigningKeyVersion as ::std::convert::TryFrom<&str>>::try_from(#id).unwrap()
547    };
548
549    output.into()
550}
551
552/// Compile-time checked `ServerName` construction.
553#[proc_macro]
554pub fn server_name(input: TokenStream) -> TokenStream {
555    let IdentifierInput { dollar_crate, id } = parse_macro_input!(input as IdentifierInput);
556    assert!(server_name::validate(&id.value()).is_ok(), "Invalid server_name");
557
558    let output = quote! {
559        <&#dollar_crate::ServerName as ::std::convert::TryFrom<&str>>::try_from(#id).unwrap()
560    };
561
562    output.into()
563}
564
565/// Compile-time checked `MxcUri` construction.
566#[proc_macro]
567pub fn mxc_uri(input: TokenStream) -> TokenStream {
568    let IdentifierInput { dollar_crate, id } = parse_macro_input!(input as IdentifierInput);
569    assert!(mxc_uri::validate(&id.value()).is_ok(), "Invalid mxc://");
570
571    let output = quote! {
572        <&#dollar_crate::MxcUri as ::std::convert::From<&str>>::from(#id)
573    };
574
575    output.into()
576}
577
578/// Compile-time checked `UserId` construction.
579///
580/// The user ID is validated using the same rules as `UserId::validate_strict()`.
581#[proc_macro]
582pub fn user_id(input: TokenStream) -> TokenStream {
583    let IdentifierInput { dollar_crate, id } = parse_macro_input!(input as IdentifierInput);
584    assert!(user_id::validate_strict(&id.value()).is_ok(), "Invalid user_id");
585
586    let output = quote! {
587        <&#dollar_crate::UserId as ::std::convert::TryFrom<&str>>::try_from(#id).unwrap()
588    };
589
590    output.into()
591}
592
593/// Compile-time checked `Base64PublicKey` construction.
594#[proc_macro]
595pub fn base64_public_key(input: TokenStream) -> TokenStream {
596    let IdentifierInput { dollar_crate, id } = parse_macro_input!(input as IdentifierInput);
597    assert!(base64_public_key::validate(&id.value()).is_ok(), "Invalid base64 public key");
598
599    let output = quote! {
600        <&#dollar_crate::DeviceKeyId as ::std::convert::TryFrom<&str>>::try_from(#id).unwrap()
601    };
602
603    output.into()
604}
605
606/// Derive the `AsRef<str>` trait for an enum.
607#[proc_macro_derive(AsRefStr, attributes(ruma_enum))]
608pub fn derive_enum_as_ref_str(input: TokenStream) -> TokenStream {
609    let input = parse_macro_input!(input as ItemEnum);
610    expand_enum_as_ref_str(&input).unwrap_or_else(syn::Error::into_compile_error).into()
611}
612
613/// Derive the `From<T: AsRef<str> + Into<Box<str>>>` trait for an enum.
614#[proc_macro_derive(FromString, attributes(ruma_enum))]
615pub fn derive_enum_from_string(input: TokenStream) -> TokenStream {
616    let input = parse_macro_input!(input as ItemEnum);
617    expand_enum_from_string(&input).unwrap_or_else(syn::Error::into_compile_error).into()
618}
619
620// FIXME: The following macros aren't actually interested in type details beyond name (and possibly
621//        generics in the future). They probably shouldn't use `DeriveInput`.
622
623/// Derive the `as_str()` method using the `AsRef<str>` implementation of the type.
624#[proc_macro_derive(AsStrAsRefStr, attributes(ruma_enum))]
625pub fn derive_as_str_as_ref_str(input: TokenStream) -> TokenStream {
626    let input = parse_macro_input!(input as DeriveInput);
627    expand_as_str_as_ref_str(&input.ident).unwrap_or_else(syn::Error::into_compile_error).into()
628}
629
630/// Derive the `fmt::Display` trait using the `AsRef<str>` implementation of the type.
631#[proc_macro_derive(DisplayAsRefStr)]
632pub fn derive_display_as_ref_str(input: TokenStream) -> TokenStream {
633    let input = parse_macro_input!(input as DeriveInput);
634    expand_display_as_ref_str(&input.ident).unwrap_or_else(syn::Error::into_compile_error).into()
635}
636
637/// Derive the `fmt::Debug` trait using the `AsRef<str>` implementation of the type.
638#[proc_macro_derive(DebugAsRefStr)]
639pub fn derive_debug_as_ref_str(input: TokenStream) -> TokenStream {
640    let input = parse_macro_input!(input as DeriveInput);
641    expand_debug_as_ref_str(&input.ident).unwrap_or_else(syn::Error::into_compile_error).into()
642}
643
644/// Derive the `Serialize` trait using the `AsRef<str>` implementation of the type.
645#[proc_macro_derive(SerializeAsRefStr)]
646pub fn derive_serialize_as_ref_str(input: TokenStream) -> TokenStream {
647    let input = parse_macro_input!(input as DeriveInput);
648    expand_serialize_as_ref_str(&input.ident).unwrap_or_else(syn::Error::into_compile_error).into()
649}
650
651/// Derive the `Deserialize` trait using the `From<Cow<str>>` implementation of the type.
652#[proc_macro_derive(DeserializeFromCowStr)]
653pub fn derive_deserialize_from_cow_str(input: TokenStream) -> TokenStream {
654    let input = parse_macro_input!(input as DeriveInput);
655    expand_deserialize_from_cow_str(&input.ident)
656        .unwrap_or_else(syn::Error::into_compile_error)
657        .into()
658}
659
660/// Derive the `PartialOrd` trait using the `AsRef<str>` implementation of the type.
661#[proc_macro_derive(PartialOrdAsRefStr)]
662pub fn derive_partial_ord_as_ref_str(input: TokenStream) -> TokenStream {
663    let input = parse_macro_input!(input as DeriveInput);
664    expand_partial_ord_as_ref_str(&input.ident)
665        .unwrap_or_else(syn::Error::into_compile_error)
666        .into()
667}
668
669/// Derive the `Ord` trait using the `AsRef<str>` implementation of the type.
670#[proc_macro_derive(OrdAsRefStr)]
671pub fn derive_ord_as_ref_str(input: TokenStream) -> TokenStream {
672    let input = parse_macro_input!(input as DeriveInput);
673    expand_ord_as_ref_str(&input.ident).unwrap_or_else(syn::Error::into_compile_error).into()
674}
675
676/// Derive the `PartialEq` trait using the `AsRef<str>` implementation of the type.
677#[proc_macro_derive(PartialEqAsRefStr)]
678pub fn derive_partial_eq_as_ref_str(input: TokenStream) -> TokenStream {
679    let input = parse_macro_input!(input as DeriveInput);
680    expand_partial_eq_as_ref_str(&input.ident).unwrap_or_else(syn::Error::into_compile_error).into()
681}
682
683/// Shorthand for the derives `AsRefStr`, `FromString`, `DisplayAsRefStr`, `DebugAsRefStr`,
684/// `SerializeAsRefStr` and `DeserializeFromCowStr`.
685#[proc_macro_derive(StringEnum, attributes(ruma_enum))]
686pub fn derive_string_enum(input: TokenStream) -> TokenStream {
687    fn expand_all(input: ItemEnum) -> syn::Result<proc_macro2::TokenStream> {
688        let as_ref_str_impl = expand_enum_as_ref_str(&input)?;
689        let from_string_impl = expand_enum_from_string(&input)?;
690        let as_str_impl = expand_as_str_as_ref_str(&input.ident)?;
691        let display_impl = expand_display_as_ref_str(&input.ident)?;
692        let debug_impl = expand_debug_as_ref_str(&input.ident)?;
693        let serialize_impl = expand_serialize_as_ref_str(&input.ident)?;
694        let deserialize_impl = expand_deserialize_from_cow_str(&input.ident)?;
695
696        Ok(quote! {
697            #as_ref_str_impl
698            #from_string_impl
699            #as_str_impl
700            #display_impl
701            #debug_impl
702            #serialize_impl
703            #deserialize_impl
704        })
705    }
706
707    let input = parse_macro_input!(input as ItemEnum);
708    expand_all(input).unwrap_or_else(syn::Error::into_compile_error).into()
709}
710
711/// A derive macro that generates no code, but registers the serde attribute so both `#[serde(...)]`
712/// and `#[cfg_attr(..., serde(...))]` are accepted on the type, its fields and (in case the input
713/// is an enum) variants fields.
714#[doc(hidden)]
715#[proc_macro_derive(_FakeDeriveSerde, attributes(serde))]
716pub fn fake_derive_serde(_input: TokenStream) -> TokenStream {
717    TokenStream::new()
718}
719
720/// > ⚠ If this is the only documentation you see, please navigate to the docs for
721/// > `ruma_common::api::request`, where actual documentation can be found.
722#[proc_macro_attribute]
723pub fn request(attr: TokenStream, item: TokenStream) -> TokenStream {
724    let attr = parse_macro_input!(attr);
725    let item = parse_macro_input!(item);
726    expand_request(attr, item).into()
727}
728
729/// > ⚠ If this is the only documentation you see, please navigate to the docs for
730/// > `ruma_common::api::response`, where actual documentation can be found.
731#[proc_macro_attribute]
732pub fn response(attr: TokenStream, item: TokenStream) -> TokenStream {
733    let attr = parse_macro_input!(attr);
734    let item = parse_macro_input!(item);
735    expand_response(attr, item).into()
736}
737
738/// Internal helper that the request macro delegates most of its work to.
739#[proc_macro_derive(Request, attributes(ruma_api))]
740pub fn derive_request(input: TokenStream) -> TokenStream {
741    let input = parse_macro_input!(input);
742    expand_derive_request(input).unwrap_or_else(syn::Error::into_compile_error).into()
743}
744
745/// Internal helper that the response macro delegates most of its work to.
746#[proc_macro_derive(Response, attributes(ruma_api))]
747pub fn derive_response(input: TokenStream) -> TokenStream {
748    let input = parse_macro_input!(input);
749    expand_derive_response(input).unwrap_or_else(syn::Error::into_compile_error).into()
750}
751
752/// A derive macro that generates no code, but registers the ruma_api attribute so both
753/// `#[ruma_api(...)]` and `#[cfg_attr(..., ruma_api(...))]` are accepted on the type, its fields
754/// and (in case the input is an enum) variants fields.
755#[doc(hidden)]
756#[proc_macro_derive(_FakeDeriveRumaApi, attributes(ruma_api))]
757pub fn fake_derive_ruma_api(_input: TokenStream) -> TokenStream {
758    TokenStream::new()
759}