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