1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
#![doc(html_favicon_url = "https://www.ruma.io/favicon.ico")]
#![doc(html_logo_url = "https://www.ruma.io/images/logo.png")]

use proc_macro::TokenStream;
use quote::quote;
use syn::{parse_macro_input, DeriveInput, ItemEnum};

use deserialize_from_cow_str::expand_deserialize_from_cow_str;
use display_as_ref_str::expand_display_as_ref_str;
use enum_as_ref_str::expand_enum_as_ref_str;
use enum_from_string::expand_enum_from_string;
use eq_as_ref_str::expand_partial_eq_as_ref_str;
use ord_as_ref_str::{expand_ord_as_ref_str, expand_partial_ord_as_ref_str};
use outgoing::expand_derive_outgoing;
use serialize_as_ref_str::expand_serialize_as_ref_str;

mod attr;
mod case;
mod deserialize_from_cow_str;
mod display_as_ref_str;
mod enum_as_ref_str;
mod enum_from_string;
mod eq_as_ref_str;
mod ord_as_ref_str;
mod outgoing;
mod serialize_as_ref_str;
mod util;

/// Derive the `Outgoing` trait, possibly generating an 'Incoming' version of the struct this
/// derive macro is used on. Specifically, if no lifetime variables are used on any of the fields
/// of the struct, this simple implementation will be generated:
///
/// ```ignore
/// # // TODO: "ignore" this doctest until it is more extensively documented. (See #541)
/// impl Outgoing for MyType {
///     type Incoming = Self;
/// }
/// ```
/*

TODO: Extend docs. Previously:

If, however, `#[wrap_incoming]` is used (which is the only reason you should ever use this
derive macro manually), a new struct `IncomingT` (where `T` is the type this derive is used on)
is generated, with all of the fields with `#[wrap_incoming]` replaced:

```ignore
#[derive(Outgoing)]
struct MyType {
    pub foo: Foo,
    #[wrap_incoming]
    pub bar: Bar,
    #[wrap_incoming(Baz)]
    pub baz: Option<Baz>,
    #[wrap_incoming(with EventResult)]
    pub x: XEvent,
    #[wrap_incoming(YEvent with EventResult)]
    pub ys: Vec<YEvent>,
}

// generated
struct IncomingMyType {
    pub foo: Foo,
    pub bar: IncomingBar,
    pub baz: Option<IncomingBaz>,
    pub x: EventResult<XEvent>,
    pub ys: Vec<EventResult<YEvent>>,
}
```

*/
#[proc_macro_derive(Outgoing, attributes(incoming_derive))]
pub fn derive_outgoing(input: TokenStream) -> TokenStream {
    let input = parse_macro_input!(input as DeriveInput);
    expand_derive_outgoing(input).unwrap_or_else(syn::Error::into_compile_error).into()
}

#[proc_macro_derive(AsRefStr, attributes(ruma_enum))]
pub fn derive_enum_as_ref_str(input: TokenStream) -> TokenStream {
    let input = parse_macro_input!(input as ItemEnum);
    expand_enum_as_ref_str(&input).unwrap_or_else(syn::Error::into_compile_error).into()
}

#[proc_macro_derive(FromString, attributes(ruma_enum))]
pub fn derive_enum_from_string(input: TokenStream) -> TokenStream {
    let input = parse_macro_input!(input as ItemEnum);
    expand_enum_from_string(&input).unwrap_or_else(syn::Error::into_compile_error).into()
}

// FIXME: The following macros aren't actually interested in type details beyond name (and possibly
//        generics in the future). They probably shouldn't use `DeriveInput`.

#[proc_macro_derive(DisplayAsRefStr)]
pub fn derive_display_as_ref_str(input: TokenStream) -> TokenStream {
    let input = parse_macro_input!(input as DeriveInput);
    expand_display_as_ref_str(&input.ident).unwrap_or_else(syn::Error::into_compile_error).into()
}

#[proc_macro_derive(SerializeAsRefStr)]
pub fn derive_serialize_as_ref_str(input: TokenStream) -> TokenStream {
    let input = parse_macro_input!(input as DeriveInput);
    expand_serialize_as_ref_str(&input.ident).unwrap_or_else(syn::Error::into_compile_error).into()
}

#[proc_macro_derive(DeserializeFromCowStr)]
pub fn derive_deserialize_from_cow_str(input: TokenStream) -> TokenStream {
    let input = parse_macro_input!(input as DeriveInput);
    expand_deserialize_from_cow_str(&input.ident)
        .unwrap_or_else(syn::Error::into_compile_error)
        .into()
}

#[proc_macro_derive(PartialOrdAsRefStr)]
pub fn derive_partial_ord_as_ref_str(input: TokenStream) -> TokenStream {
    let input = parse_macro_input!(input as DeriveInput);
    expand_partial_ord_as_ref_str(&input.ident)
        .unwrap_or_else(syn::Error::into_compile_error)
        .into()
}

#[proc_macro_derive(OrdAsRefStr)]
pub fn derive_ord_as_ref_str(input: TokenStream) -> TokenStream {
    let input = parse_macro_input!(input as DeriveInput);
    expand_ord_as_ref_str(&input.ident).unwrap_or_else(syn::Error::into_compile_error).into()
}

#[proc_macro_derive(PartialEqAsRefStr)]
pub fn derive_partial_eq_as_ref_str(input: TokenStream) -> TokenStream {
    let input = parse_macro_input!(input as DeriveInput);
    expand_partial_eq_as_ref_str(&input.ident).unwrap_or_else(syn::Error::into_compile_error).into()
}

/// Shorthand for the derives `AsRefStr`, `FromString`, `DisplayAsRefStr`, `SerializeAsRefStr` and
/// `DeserializeFromCowStr`.
#[proc_macro_derive(StringEnum, attributes(ruma_enum))]
pub fn derive_string_enum(input: TokenStream) -> TokenStream {
    fn expand_all(input: ItemEnum) -> syn::Result<proc_macro2::TokenStream> {
        let as_ref_str_impl = expand_enum_as_ref_str(&input)?;
        let from_string_impl = expand_enum_from_string(&input)?;
        let display_impl = expand_display_as_ref_str(&input.ident)?;
        let serialize_impl = expand_serialize_as_ref_str(&input.ident)?;
        let deserialize_impl = expand_deserialize_from_cow_str(&input.ident)?;

        Ok(quote! {
            #as_ref_str_impl
            #from_string_impl
            #display_impl
            #serialize_impl
            #deserialize_impl
        })
    }

    let input = parse_macro_input!(input as ItemEnum);
    expand_all(input).unwrap_or_else(syn::Error::into_compile_error).into()
}

/// A derive macro that generates no code, but registers the serde attribute so both `#[serde(...)]`
/// and `#[cfg_attr(..., serde(...))]` are accepted on the type, its fields and (in case the input
/// is an enum) variants fields.
#[doc(hidden)]
#[proc_macro_derive(_FakeDeriveSerde, attributes(serde))]
pub fn fake_derive_serde(_input: TokenStream) -> TokenStream {
    TokenStream::new()
}