ruma_serde_macros/
lib.rs

1#![doc(html_favicon_url = "https://www.ruma.io/favicon.ico")]
2#![doc(html_logo_url = "https://www.ruma.io/images/logo.png")]
3
4use proc_macro::TokenStream;
5use quote::quote;
6use syn::{parse_macro_input, DeriveInput, ItemEnum};
7
8use self::{
9    deserialize_from_cow_str::expand_deserialize_from_cow_str,
10    display_as_ref_str::expand_display_as_ref_str,
11    enum_as_ref_str::expand_enum_as_ref_str,
12    enum_from_string::expand_enum_from_string,
13    eq_as_ref_str::expand_partial_eq_as_ref_str,
14    ord_as_ref_str::{expand_ord_as_ref_str, expand_partial_ord_as_ref_str},
15    outgoing::expand_derive_outgoing,
16    serialize_as_ref_str::expand_serialize_as_ref_str,
17};
18
19mod attr;
20mod case;
21mod deserialize_from_cow_str;
22mod display_as_ref_str;
23mod enum_as_ref_str;
24mod enum_from_string;
25mod eq_as_ref_str;
26mod ord_as_ref_str;
27mod outgoing;
28mod serialize_as_ref_str;
29mod util;
30
31/// Derive the `Outgoing` trait, possibly generating an 'Incoming' version of the struct this
32/// derive macro is used on.
33///
34/// Specifically, if no lifetime variables are used on any of the fields of the struct, this simple
35/// implementation will be generated:
36///
37/// ```ignore
38/// # // TODO: "ignore" this doctest until it is more extensively documented. (See #541)
39/// impl Outgoing for MyType {
40///     type Incoming = Self;
41/// }
42/// ```
43/*
44
45TODO: Extend docs. Previously:
46
47If, however, `#[wrap_incoming]` is used (which is the only reason you should ever use this
48derive macro manually), a new struct `IncomingT` (where `T` is the type this derive is used on)
49is generated, with all of the fields with `#[wrap_incoming]` replaced:
50
51```ignore
52#[derive(Outgoing)]
53struct MyType {
54    pub foo: Foo,
55    #[wrap_incoming]
56    pub bar: Bar,
57    #[wrap_incoming(Baz)]
58    pub baz: Option<Baz>,
59    #[wrap_incoming(with EventResult)]
60    pub x: XEvent,
61    #[wrap_incoming(YEvent with EventResult)]
62    pub ys: Vec<YEvent>,
63}
64
65// generated
66struct IncomingMyType {
67    pub foo: Foo,
68    pub bar: IncomingBar,
69    pub baz: Option<IncomingBaz>,
70    pub x: EventResult<XEvent>,
71    pub ys: Vec<EventResult<YEvent>>,
72}
73```
74
75*/
76#[proc_macro_derive(Outgoing, attributes(incoming_derive))]
77pub fn derive_outgoing(input: TokenStream) -> TokenStream {
78    let input = parse_macro_input!(input as DeriveInput);
79    expand_derive_outgoing(input).unwrap_or_else(syn::Error::into_compile_error).into()
80}
81
82#[proc_macro_derive(AsRefStr, attributes(ruma_enum))]
83pub fn derive_enum_as_ref_str(input: TokenStream) -> TokenStream {
84    let input = parse_macro_input!(input as ItemEnum);
85    expand_enum_as_ref_str(&input).unwrap_or_else(syn::Error::into_compile_error).into()
86}
87
88#[proc_macro_derive(FromString, attributes(ruma_enum))]
89pub fn derive_enum_from_string(input: TokenStream) -> TokenStream {
90    let input = parse_macro_input!(input as ItemEnum);
91    expand_enum_from_string(&input).unwrap_or_else(syn::Error::into_compile_error).into()
92}
93
94// FIXME: The following macros aren't actually interested in type details beyond name (and possibly
95//        generics in the future). They probably shouldn't use `DeriveInput`.
96
97#[proc_macro_derive(DisplayAsRefStr)]
98pub fn derive_display_as_ref_str(input: TokenStream) -> TokenStream {
99    let input = parse_macro_input!(input as DeriveInput);
100    expand_display_as_ref_str(&input.ident).unwrap_or_else(syn::Error::into_compile_error).into()
101}
102
103#[proc_macro_derive(SerializeAsRefStr)]
104pub fn derive_serialize_as_ref_str(input: TokenStream) -> TokenStream {
105    let input = parse_macro_input!(input as DeriveInput);
106    expand_serialize_as_ref_str(&input.ident).unwrap_or_else(syn::Error::into_compile_error).into()
107}
108
109#[proc_macro_derive(DeserializeFromCowStr)]
110pub fn derive_deserialize_from_cow_str(input: TokenStream) -> TokenStream {
111    let input = parse_macro_input!(input as DeriveInput);
112    expand_deserialize_from_cow_str(&input.ident)
113        .unwrap_or_else(syn::Error::into_compile_error)
114        .into()
115}
116
117#[proc_macro_derive(PartialOrdAsRefStr)]
118pub fn derive_partial_ord_as_ref_str(input: TokenStream) -> TokenStream {
119    let input = parse_macro_input!(input as DeriveInput);
120    expand_partial_ord_as_ref_str(&input.ident)
121        .unwrap_or_else(syn::Error::into_compile_error)
122        .into()
123}
124
125#[proc_macro_derive(OrdAsRefStr)]
126pub fn derive_ord_as_ref_str(input: TokenStream) -> TokenStream {
127    let input = parse_macro_input!(input as DeriveInput);
128    expand_ord_as_ref_str(&input.ident).unwrap_or_else(syn::Error::into_compile_error).into()
129}
130
131#[proc_macro_derive(PartialEqAsRefStr)]
132pub fn derive_partial_eq_as_ref_str(input: TokenStream) -> TokenStream {
133    let input = parse_macro_input!(input as DeriveInput);
134    expand_partial_eq_as_ref_str(&input.ident).unwrap_or_else(syn::Error::into_compile_error).into()
135}
136
137/// Shorthand for the derives `AsRefStr`, `FromString`, `DisplayAsRefStr`, `SerializeAsRefStr` and
138/// `DeserializeFromCowStr`.
139#[proc_macro_derive(StringEnum, attributes(ruma_enum))]
140pub fn derive_string_enum(input: TokenStream) -> TokenStream {
141    fn expand_all(input: ItemEnum) -> syn::Result<proc_macro2::TokenStream> {
142        let as_ref_str_impl = expand_enum_as_ref_str(&input)?;
143        let from_string_impl = expand_enum_from_string(&input)?;
144        let display_impl = expand_display_as_ref_str(&input.ident)?;
145        let serialize_impl = expand_serialize_as_ref_str(&input.ident)?;
146        let deserialize_impl = expand_deserialize_from_cow_str(&input.ident)?;
147
148        Ok(quote! {
149            #as_ref_str_impl
150            #from_string_impl
151            #display_impl
152            #serialize_impl
153            #deserialize_impl
154        })
155    }
156
157    let input = parse_macro_input!(input as ItemEnum);
158    expand_all(input).unwrap_or_else(syn::Error::into_compile_error).into()
159}
160
161/// A derive macro that generates no code, but registers the serde attribute so both `#[serde(...)]`
162/// and `#[cfg_attr(..., serde(...))]` are accepted on the type, its fields and (in case the input
163/// is an enum) variants fields.
164#[doc(hidden)]
165#[proc_macro_derive(_FakeDeriveSerde, attributes(serde))]
166pub fn fake_derive_serde(_input: TokenStream) -> TokenStream {
167    TokenStream::new()
168}