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
//! Crate ruma-api-macros provides a procedural macro for easily generating
//! [ruma-api](https://github.com/ruma/ruma-api)-compatible endpoints.
//!
//! This crate should never be used directly; instead, use it through the
//! re-exports in ruma-api. Also note that for technical reasons, the
//! `ruma_api!` macro is only documented in ruma-api, not here.

#![deny(missing_copy_implementations, missing_debug_implementations)]
#![allow(clippy::cognitive_complexity)]
#![recursion_limit = "256"]

extern crate proc_macro;

use std::convert::TryFrom as _;

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

use self::{
    api::{Api, RawApi},
    derive_outgoing::expand_derive_outgoing,
};

mod api;
mod derive_outgoing;

#[proc_macro]
pub fn ruma_api(input: TokenStream) -> TokenStream {
    let raw_api = parse_macro_input!(input as RawApi);
    match Api::try_from(raw_api) {
        Ok(api) => api.into_token_stream().into(),
        Err(err) => err.to_compile_error().into(),
    }
}

/// Derive the `Outgoing` trait, possibly generating an 'Incoming' version of the struct this
/// derive macro is used on. Specifically, if no `#[wrap_incoming]` attribute is used on any of the
/// fields of the struct, this simple implementation will be generated:
///
/// ```ignore
/// impl Outgoing for MyType {
///     type Incoming = Self;
/// }
/// ```
///
/// 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>>,
/// }
/// ```
// TODO: Make it clear that `#[wrap_incoming]` and `#[wrap_incoming(Type)]` without the "with" part
// are (only) useful for fallible deserialization of nested structures.
#[proc_macro_derive(Outgoing, attributes(wrap_incoming, incoming_no_deserialize))]
pub fn derive_outgoing(input: TokenStream) -> TokenStream {
    let input = parse_macro_input!(input as DeriveInput);
    expand_derive_outgoing(input).unwrap_or_else(|err| err.to_compile_error()).into()
}