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
//! Convenience macros for [paperclip](https://github.com/paperclip-rs/paperclip).
//!
//! You shouldn't need to depend on this, because the stuff here is
//! already exposed by the corresponding crates.

#![recursion_limit = "512"]

extern crate proc_macro;
#[macro_use]
extern crate proc_macro_error;

#[cfg(feature = "actix")]
#[macro_use]
mod actix;
#[cfg(feature = "v2")]
mod core;

use proc_macro::TokenStream;
use quote::quote;
use syn::{
    parse::{Parse, ParseStream},
    punctuated::Punctuated,
    spanned::Spanned,
    token::Comma,
    DeriveInput, NestedMeta,
};

/// Converts your struct to support deserializing from an OpenAPI v2
/// [Schema](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/2.0.md#schemaObject)
/// object ([example](https://paperclip-rs.github.io/paperclip/paperclip/v2/)). This adds the necessary fields (in addition to your own fields) and implements the
/// `Schema` trait for parsing and codegen.
#[cfg(feature = "v2")]
#[proc_macro_attribute]
pub fn api_v2_schema_struct(_attr: TokenStream, input: TokenStream) -> TokenStream {
    self::core::emit_v2_schema_struct(input)
}

/// Marker attribute for indicating that a function is an OpenAPI v2 compatible operation.
#[cfg(feature = "actix")]
#[proc_macro_error]
#[proc_macro_attribute]
pub fn api_v2_operation(attr: TokenStream, input: TokenStream) -> TokenStream {
    self::actix::emit_v2_operation(attr, input)
}

/// Derive attribute for indicating that a type is an OpenAPI v2 compatible definition.
#[cfg(feature = "actix")]
#[proc_macro_error]
#[proc_macro_derive(Apiv2Schema, attributes(openapi))]
pub fn api_v2_schema(input: TokenStream) -> TokenStream {
    self::actix::emit_v2_definition(input)
}

/// Marker attribute for indicating that an object forbids public access to operation (for example AccessToken).
#[cfg(feature = "actix")]
#[proc_macro_error]
#[proc_macro_derive(Apiv2Security, attributes(openapi))]
pub fn api_v2_security(input: TokenStream) -> TokenStream {
    self::actix::emit_v2_security(input)
}

/// Derive attribute for indicating that a type is an OpenAPI v2 compatible header parameter.
#[cfg(feature = "actix")]
#[proc_macro_error]
#[proc_macro_derive(Apiv2Header, attributes(openapi))]
pub fn api_v2_header(input: TokenStream) -> TokenStream {
    self::actix::emit_v2_header(input)
}

/// Marker attribute for indicating that the marked object can represent non-2xx (error)
/// status codes with optional descriptions.
#[cfg(feature = "actix")]
#[proc_macro_error]
#[proc_macro_attribute]
pub fn api_v2_errors(attrs: TokenStream, input: TokenStream) -> TokenStream {
    self::actix::emit_v2_errors(attrs, input)
}

/// Marker attribute for indicating that the marked object can filter error responses from the
/// the `#[api_v2_errors]` macro.
#[cfg(feature = "actix")]
#[proc_macro_error]
#[proc_macro_attribute]
pub fn api_v2_errors_overlay(attrs: TokenStream, input: TokenStream) -> TokenStream {
    self::actix::emit_v2_errors_overlay(attrs, input)
}

/// Generate an error at the call site and return empty token stream.
#[allow(dead_code)]
fn span_error_with_msg<T: Spanned>(it: &T, msg: &str) -> TokenStream {
    emit_error!(it.span().unwrap(), msg);
    (quote! {}).into()
}

/// Parses this token stream expecting a struct/enum and fails with an error otherwise.
#[allow(dead_code)]
fn expect_struct_or_enum(ts: TokenStream) -> Result<DeriveInput, TokenStream> {
    syn::parse(ts).map_err(|e| {
        emit_error!(
            e.span().unwrap(),
            "expected struct or enum for deriving schema."
        );
        quote!().into()
    })
}

/// Helper struct for parsing proc-macro input attributes.
#[derive(Default)]
struct MacroAttribute(Punctuated<NestedMeta, Comma>);

impl Parse for MacroAttribute {
    fn parse(input: ParseStream) -> syn::Result<Self> {
        Ok(MacroAttribute(input.call(Punctuated::parse_terminated)?))
    }
}

#[cfg(feature = "actix")]
fn parse_input_attrs(ts: TokenStream) -> MacroAttribute {
    syn::parse(ts)
        .map_err(|e| {
            emit_warning!(
                e.span().unwrap(),
                "cannot parse proc-macro input attributes."
            );
        })
        .ok()
        .unwrap_or_default()
}

#[cfg(feature = "actix")]
rest_methods! {
    Get,    get,
    Post,   post,
    Put,    put,
    Delete, delete,
    Patch,  patch,
    Head,   head,
}