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.
67#![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)]
1213use 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};
2223mod api;
24mod events;
25mod identifiers;
26mod serde;
27mod util;
2829use 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};
5556/// Generates an enum to represent the various Matrix event types.
57///
58/// This macro also implements the necessary traits for the type to serialize and deserialize
59/// itself.
60///
61/// By default, the types generated by this macro get a `#[non_exhaustive]` attribute. This
62/// behavior can be controlled by setting the `ruma_unstable_exhaustive_types` compile-time
63/// `cfg` setting as `--cfg=ruma_unstable_exhaustive_types` using `RUSTFLAGS` or
64/// `.cargo/config.toml` (under `[build]` -> `rustflags = ["..."]`). When that setting is
65/// activated, the attribute is not applied so the types are exhaustive.
66///
67/// # Examples
68///
69/// ```ignore
70/// # // HACK: This is "ignore" because of cyclical dependency drama.
71/// use ruma_macros::event_enum;
72///
73/// event_enum! {
74/// enum ToDevice {
75/// "m.any.event",
76/// "m.other.event",
77/// }
78///
79/// enum State {
80/// "m.more.events",
81/// "m.different.event",
82/// }
83/// }
84/// ```
85/// (The enum name has to be a valid identifier for `<EventKind as Parse>::parse`)
86///// TODO: Change above (`<EventKind as Parse>::parse`) to [] after fully qualified syntax is
87///// supported: https://github.com/rust-lang/rust/issues/74563
88#[proc_macro]
89pub fn event_enum(input: TokenStream) -> TokenStream {
90let event_enum_input = syn::parse_macro_input!(input as EventEnumInput);
9192let ruma_common = import_ruma_common();
9394let enums = event_enum_input
95 .enums
96 .iter()
97 .map(|e| expand_event_enums(e).unwrap_or_else(syn::Error::into_compile_error))
98 .collect::<pm2::TokenStream>();
99100let event_types = expand_event_type_enum(event_enum_input, ruma_common)
101 .unwrap_or_else(syn::Error::into_compile_error);
102103let tokens = quote! {
104 #enums
105 #event_types
106 };
107108 tokens.into()
109}
110111/// Generates an implementation of `ruma_events::EventContent`.
112///
113/// Also generates type aliases depending on the kind of event, with the final `Content` of the type
114/// name removed and prefixed added. For instance, a message-like event content type
115/// `FooEventContent` will have the following aliases generated:
116///
117/// * `type FooEvent = MessageLikeEvent<FooEventContent>`
118/// * `type SyncFooEvent = SyncMessageLikeEvent<FooEventContent>`
119/// * `type OriginalFooEvent = OriginalMessageLikeEvent<FooEventContent>`
120/// * `type OriginalSyncFooEvent = OriginalSyncMessageLikeEvent<FooEventContent>`
121/// * `type RedactedFooEvent = RedactedMessageLikeEvent<FooEventContent>`
122/// * `type RedactedSyncFooEvent = RedactedSyncMessageLikeEvent<FooEventContent>`
123///
124/// You can use `cargo doc` to find out more details, its `--document-private-items` flag also lets
125/// you generate documentation for binaries or private parts of a library.
126///
127/// By default, the type this macro is used on and the generated types get a `#[non_exhaustive]`
128/// attribute. This behavior can be controlled by setting the `ruma_unstable_exhaustive_types`
129/// compile-time `cfg` setting as `--cfg=ruma_unstable_exhaustive_types` using `RUSTFLAGS` or
130/// `.cargo/config.toml` (under `[build]` -> `rustflags = ["..."]`). When that setting is
131/// activated, the attribute is not applied so the types are exhaustive.
132#[proc_macro_derive(EventContent, attributes(ruma_event))]
133pub fn derive_event_content(input: TokenStream) -> TokenStream {
134let ruma_events = import_ruma_events();
135let input = parse_macro_input!(input as DeriveInput);
136137 expand_event_content(&input, &ruma_events).unwrap_or_else(syn::Error::into_compile_error).into()
138}
139140/// Generates implementations needed to serialize and deserialize Matrix events.
141#[proc_macro_derive(Event, attributes(ruma_event))]
142pub fn derive_event(input: TokenStream) -> TokenStream {
143let input = parse_macro_input!(input as DeriveInput);
144 expand_event(input).unwrap_or_else(syn::Error::into_compile_error).into()
145}
146147/// Generates `From` implementations for event enums.
148#[proc_macro_derive(EventEnumFromEvent)]
149pub fn derive_from_event_to_enum(input: TokenStream) -> TokenStream {
150let input = parse_macro_input!(input as DeriveInput);
151 expand_from_impls_derived(input).into()
152}
153154/// Generate methods and trait impl's for ZST identifier type.
155///
156/// This macro generates an `Owned*` wrapper type for the identifier type. This wrapper type is
157/// variable, by default it'll use [`Box`], but it can be changed at compile time
158/// by setting `--cfg=ruma_identifiers_storage=...` using `RUSTFLAGS` or `.cargo/config.toml` (under
159/// `[build]` -> `rustflags = ["..."]`). Currently the only supported value is `Arc`, that uses
160/// [`Arc`](std::sync::Arc) as a wrapper type.
161///
162/// This macro implements:
163///
164/// * Conversions to and from string types, `AsRef<[u8]>` and `AsRef<str>`, as well as `as_str()`
165/// and `as_bytes()` methods. The borrowed type can be converted from a borrowed string without
166/// allocation.
167/// * Conversions to and from borrowed and owned type.
168/// * `Deref`, `AsRef` and `Borrow` to the borrowed type for the owned type.
169/// * `PartialEq` implementations for testing equality with string types and owned and borrowed
170/// types.
171///
172/// # Attributes
173///
174/// * `#[ruma_api(validate = PATH)]`: the path to a function to validate the string during parsing
175/// and deserialization. By default, the types implement `From` string types, when this is set
176/// they implement `TryFrom`.
177///
178/// # Examples
179///
180/// ```ignore
181/// # // HACK: This is "ignore" because of cyclical dependency drama.
182/// use ruma_macros::IdZst;
183///
184/// #[derive(PartialEq, Eq, PartialOrd, Ord, Hash, IdZst)]
185/// #[ruma_id(validate = ruma_identifiers_validation::user_id::validate)]
186/// pub struct UserId(str);
187/// ```
188#[proc_macro_derive(IdZst, attributes(ruma_id))]
189pub fn derive_id_zst(input: TokenStream) -> TokenStream {
190let input = parse_macro_input!(input as ItemStruct);
191 expand_id_zst(input).unwrap_or_else(syn::Error::into_compile_error).into()
192}
193194/// Compile-time checked `EventId` construction.
195#[proc_macro]
196pub fn event_id(input: TokenStream) -> TokenStream {
197let IdentifierInput { dollar_crate, id } = parse_macro_input!(input as IdentifierInput);
198assert!(event_id::validate(&id.value()).is_ok(), "Invalid event id");
199200let output = quote! {
201 <&#dollar_crate::EventId as ::std::convert::TryFrom<&str>>::try_from(#id).unwrap()
202 };
203204 output.into()
205}
206207/// Compile-time checked `RoomAliasId` construction.
208#[proc_macro]
209pub fn room_alias_id(input: TokenStream) -> TokenStream {
210let IdentifierInput { dollar_crate, id } = parse_macro_input!(input as IdentifierInput);
211assert!(room_alias_id::validate(&id.value()).is_ok(), "Invalid room_alias_id");
212213let output = quote! {
214 <&#dollar_crate::RoomAliasId as ::std::convert::TryFrom<&str>>::try_from(#id).unwrap()
215 };
216217 output.into()
218}
219220/// Compile-time checked `RoomId` construction.
221#[proc_macro]
222pub fn room_id(input: TokenStream) -> TokenStream {
223let IdentifierInput { dollar_crate, id } = parse_macro_input!(input as IdentifierInput);
224assert!(room_id::validate(&id.value()).is_ok(), "Invalid room_id");
225226let output = quote! {
227 <&#dollar_crate::RoomId as ::std::convert::TryFrom<&str>>::try_from(#id).unwrap()
228 };
229230 output.into()
231}
232233/// Compile-time checked `RoomVersionId` construction.
234#[proc_macro]
235pub fn room_version_id(input: TokenStream) -> TokenStream {
236let IdentifierInput { dollar_crate, id } = parse_macro_input!(input as IdentifierInput);
237assert!(room_version_id::validate(&id.value()).is_ok(), "Invalid room_version_id");
238239let output = quote! {
240 <#dollar_crate::RoomVersionId as ::std::convert::TryFrom<&str>>::try_from(#id).unwrap()
241 };
242243 output.into()
244}
245246/// Compile-time checked `ServerSigningKeyVersion` construction.
247#[proc_macro]
248pub fn server_signing_key_version(input: TokenStream) -> TokenStream {
249let IdentifierInput { dollar_crate, id } = parse_macro_input!(input as IdentifierInput);
250assert!(
251 server_signing_key_version::validate(&id.value()).is_ok(),
252"Invalid server_signing_key_version"
253);
254255let output = quote! {
256 <&#dollar_crate::ServerSigningKeyVersion as ::std::convert::TryFrom<&str>>::try_from(#id).unwrap()
257 };
258259 output.into()
260}
261262/// Compile-time checked `ServerName` construction.
263#[proc_macro]
264pub fn server_name(input: TokenStream) -> TokenStream {
265let IdentifierInput { dollar_crate, id } = parse_macro_input!(input as IdentifierInput);
266assert!(server_name::validate(&id.value()).is_ok(), "Invalid server_name");
267268let output = quote! {
269 <&#dollar_crate::ServerName as ::std::convert::TryFrom<&str>>::try_from(#id).unwrap()
270 };
271272 output.into()
273}
274275/// Compile-time checked `MxcUri` construction.
276#[proc_macro]
277pub fn mxc_uri(input: TokenStream) -> TokenStream {
278let IdentifierInput { dollar_crate, id } = parse_macro_input!(input as IdentifierInput);
279assert!(mxc_uri::validate(&id.value()).is_ok(), "Invalid mxc://");
280281let output = quote! {
282 <&#dollar_crate::MxcUri as ::std::convert::From<&str>>::from(#id)
283 };
284285 output.into()
286}
287288/// Compile-time checked `UserId` construction.
289///
290/// The user ID is validated using the same rules as `UserId::validate_strict()`.
291#[proc_macro]
292pub fn user_id(input: TokenStream) -> TokenStream {
293let IdentifierInput { dollar_crate, id } = parse_macro_input!(input as IdentifierInput);
294assert!(user_id::validate_strict(&id.value()).is_ok(), "Invalid user_id");
295296let output = quote! {
297 <&#dollar_crate::UserId as ::std::convert::TryFrom<&str>>::try_from(#id).unwrap()
298 };
299300 output.into()
301}
302303/// Compile-time checked `Base64PublicKey` construction.
304#[proc_macro]
305pub fn base64_public_key(input: TokenStream) -> TokenStream {
306let IdentifierInput { dollar_crate, id } = parse_macro_input!(input as IdentifierInput);
307assert!(base64_public_key::validate(&id.value()).is_ok(), "Invalid base64 public key");
308309let output = quote! {
310 <&#dollar_crate::DeviceKeyId as ::std::convert::TryFrom<&str>>::try_from(#id).unwrap()
311 };
312313 output.into()
314}
315316/// Derive the `AsRef<str>` trait for an enum.
317#[proc_macro_derive(AsRefStr, attributes(ruma_enum))]
318pub fn derive_enum_as_ref_str(input: TokenStream) -> TokenStream {
319let input = parse_macro_input!(input as ItemEnum);
320 expand_enum_as_ref_str(&input).unwrap_or_else(syn::Error::into_compile_error).into()
321}
322323/// Derive the `From<T: AsRef<str> + Into<Box<str>>>` trait for an enum.
324#[proc_macro_derive(FromString, attributes(ruma_enum))]
325pub fn derive_enum_from_string(input: TokenStream) -> TokenStream {
326let input = parse_macro_input!(input as ItemEnum);
327 expand_enum_from_string(&input).unwrap_or_else(syn::Error::into_compile_error).into()
328}
329330// FIXME: The following macros aren't actually interested in type details beyond name (and possibly
331// generics in the future). They probably shouldn't use `DeriveInput`.
332333/// Derive the `as_str()` method using the `AsRef<str>` implementation of the type.
334#[proc_macro_derive(AsStrAsRefStr, attributes(ruma_enum))]
335pub fn derive_as_str_as_ref_str(input: TokenStream) -> TokenStream {
336let input = parse_macro_input!(input as DeriveInput);
337 expand_as_str_as_ref_str(&input.ident).unwrap_or_else(syn::Error::into_compile_error).into()
338}
339340/// Derive the `fmt::Display` trait using the `AsRef<str>` implementation of the type.
341#[proc_macro_derive(DisplayAsRefStr)]
342pub fn derive_display_as_ref_str(input: TokenStream) -> TokenStream {
343let input = parse_macro_input!(input as DeriveInput);
344 expand_display_as_ref_str(&input.ident).unwrap_or_else(syn::Error::into_compile_error).into()
345}
346347/// Derive the `fmt::Debug` trait using the `AsRef<str>` implementation of the type.
348#[proc_macro_derive(DebugAsRefStr)]
349pub fn derive_debug_as_ref_str(input: TokenStream) -> TokenStream {
350let input = parse_macro_input!(input as DeriveInput);
351 expand_debug_as_ref_str(&input.ident).unwrap_or_else(syn::Error::into_compile_error).into()
352}
353354/// Derive the `Serialize` trait using the `AsRef<str>` implementation of the type.
355#[proc_macro_derive(SerializeAsRefStr)]
356pub fn derive_serialize_as_ref_str(input: TokenStream) -> TokenStream {
357let input = parse_macro_input!(input as DeriveInput);
358 expand_serialize_as_ref_str(&input.ident).unwrap_or_else(syn::Error::into_compile_error).into()
359}
360361/// Derive the `Deserialize` trait using the `From<Cow<str>>` implementation of the type.
362#[proc_macro_derive(DeserializeFromCowStr)]
363pub fn derive_deserialize_from_cow_str(input: TokenStream) -> TokenStream {
364let input = parse_macro_input!(input as DeriveInput);
365 expand_deserialize_from_cow_str(&input.ident)
366 .unwrap_or_else(syn::Error::into_compile_error)
367 .into()
368}
369370/// Derive the `PartialOrd` trait using the `AsRef<str>` implementation of the type.
371#[proc_macro_derive(PartialOrdAsRefStr)]
372pub fn derive_partial_ord_as_ref_str(input: TokenStream) -> TokenStream {
373let input = parse_macro_input!(input as DeriveInput);
374 expand_partial_ord_as_ref_str(&input.ident)
375 .unwrap_or_else(syn::Error::into_compile_error)
376 .into()
377}
378379/// Derive the `Ord` trait using the `AsRef<str>` implementation of the type.
380#[proc_macro_derive(OrdAsRefStr)]
381pub fn derive_ord_as_ref_str(input: TokenStream) -> TokenStream {
382let input = parse_macro_input!(input as DeriveInput);
383 expand_ord_as_ref_str(&input.ident).unwrap_or_else(syn::Error::into_compile_error).into()
384}
385386/// Derive the `PartialEq` trait using the `AsRef<str>` implementation of the type.
387#[proc_macro_derive(PartialEqAsRefStr)]
388pub fn derive_partial_eq_as_ref_str(input: TokenStream) -> TokenStream {
389let input = parse_macro_input!(input as DeriveInput);
390 expand_partial_eq_as_ref_str(&input.ident).unwrap_or_else(syn::Error::into_compile_error).into()
391}
392393/// Shorthand for the derives `AsRefStr`, `FromString`, `DisplayAsRefStr`, `DebugAsRefStr`,
394/// `SerializeAsRefStr` and `DeserializeFromCowStr`.
395#[proc_macro_derive(StringEnum, attributes(ruma_enum))]
396pub fn derive_string_enum(input: TokenStream) -> TokenStream {
397fn expand_all(input: ItemEnum) -> syn::Result<proc_macro2::TokenStream> {
398let as_ref_str_impl = expand_enum_as_ref_str(&input)?;
399let from_string_impl = expand_enum_from_string(&input)?;
400let as_str_impl = expand_as_str_as_ref_str(&input.ident)?;
401let display_impl = expand_display_as_ref_str(&input.ident)?;
402let debug_impl = expand_debug_as_ref_str(&input.ident)?;
403let serialize_impl = expand_serialize_as_ref_str(&input.ident)?;
404let deserialize_impl = expand_deserialize_from_cow_str(&input.ident)?;
405406Ok(quote! {
407 #as_ref_str_impl
408 #from_string_impl
409 #as_str_impl
410 #display_impl
411 #debug_impl
412 #serialize_impl
413 #deserialize_impl
414 })
415 }
416417let input = parse_macro_input!(input as ItemEnum);
418 expand_all(input).unwrap_or_else(syn::Error::into_compile_error).into()
419}
420421/// A derive macro that generates no code, but registers the serde attribute so both `#[serde(...)]`
422/// and `#[cfg_attr(..., serde(...))]` are accepted on the type, its fields and (in case the input
423/// is an enum) variants fields.
424#[doc(hidden)]
425#[proc_macro_derive(_FakeDeriveSerde, attributes(serde))]
426pub fn fake_derive_serde(_input: TokenStream) -> TokenStream {
427 TokenStream::new()
428}
429430/// > ⚠ If this is the only documentation you see, please navigate to the docs for
431/// > `ruma_common::api::request`, where actual documentation can be found.
432#[proc_macro_attribute]
433pub fn request(attr: TokenStream, item: TokenStream) -> TokenStream {
434let attr = parse_macro_input!(attr);
435let item = parse_macro_input!(item);
436 expand_request(attr, item).into()
437}
438439/// > ⚠ If this is the only documentation you see, please navigate to the docs for
440/// > `ruma_common::api::response`, where actual documentation can be found.
441#[proc_macro_attribute]
442pub fn response(attr: TokenStream, item: TokenStream) -> TokenStream {
443let attr = parse_macro_input!(attr);
444let item = parse_macro_input!(item);
445 expand_response(attr, item).into()
446}
447448/// Internal helper that the request macro delegates most of its work to.
449#[proc_macro_derive(Request, attributes(ruma_api))]
450pub fn derive_request(input: TokenStream) -> TokenStream {
451let input = parse_macro_input!(input);
452 expand_derive_request(input).unwrap_or_else(syn::Error::into_compile_error).into()
453}
454455/// Internal helper that the response macro delegates most of its work to.
456#[proc_macro_derive(Response, attributes(ruma_api))]
457pub fn derive_response(input: TokenStream) -> TokenStream {
458let input = parse_macro_input!(input);
459 expand_derive_response(input).unwrap_or_else(syn::Error::into_compile_error).into()
460}
461462/// A derive macro that generates no code, but registers the ruma_api attribute so both
463/// `#[ruma_api(...)]` and `#[cfg_attr(..., ruma_api(...))]` are accepted on the type, its fields
464/// and (in case the input is an enum) variants fields.
465#[doc(hidden)]
466#[proc_macro_derive(_FakeDeriveRumaApi, attributes(ruma_api))]
467pub fn fake_derive_ruma_api(_input: TokenStream) -> TokenStream {
468 TokenStream::new()
469}