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