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