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