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