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
extern crate proc_macro;

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

mod cookies;
mod forms;
mod route;

#[proc_macro_attribute]
pub fn cookie(meta: TokenStream, item: TokenStream) -> TokenStream {
    let ast = parse_macro_input!(item as syn::ItemStruct);
    let meta = parse_macro_input!(meta as cookies::CookieMeta);
    let cookie = cookies::cookie(&meta, &ast);
    let mut tokens = ast.to_token_stream();
    tokens.extend(cookie);
    TokenStream::from(tokens)
}

#[proc_macro_attribute]
pub fn form(meta: TokenStream, item: TokenStream) -> TokenStream {
    let mut ast = parse_macro_input!(item as syn::ItemStruct);
    let meta = parse_macro_input!(meta as forms::FormMeta);
    let display = forms::form(&meta, &mut ast);
    let mut tokens = ast.to_token_stream();
    tokens.extend(display);
    TokenStream::from(tokens)
}

/// Implement a request handler wrapper for the annotated function
///
/// The attribute takes allowed methods as its arguments:
///
/// ```ignore
/// /// This handler will immediately return a `405 Method not allowed`
/// /// error for all request methods other than `GET`
/// #[handler(GET)]
/// fn hello(_: &App) -> Result<Response<String>, Error> {
///     Ok(Response::builder()
///         .status(StatusCode::OK)
///         .body("Hello, world".into())
///         .unwrap())
/// }
/// ```
///
/// The first argument of the function must be a reference to an implementer of
/// the `Application` trait (the implementor may also be wrapped in an `Arc`).
/// All unannotated arguments must be of types that implement the `FromContext`
/// trait for the `Application` type used in the first argument. This includes
/// `&http::request::Parts`, the `Request`'s headers and any number of types
/// that can represent a path component from the URI:
///
/// * `&[u8]` for the bytes representation of the path component
/// * `Cow<'_, str>`
/// * `String`
/// * Numeric types (`i8`, `u8`, `i16`, `u16`, ..., `isize`, `usize`, `f32`, `f64`)
/// * `bool` and `char`
/// * If the `hyper` feature is enabled, `hyper::body::Body`
///   (only if `Application::RequestBody` is also `Body`)
///
/// Each of these types can be wrapped in `Option` for optional path components.
/// Additionally, there are two attributes that may be used on handler arguments:
///
/// * `#[rest]`: a `&str` representing the part of the request path not yet consumed by routing
/// * `#[query]`: a type that implements `Deserialize`, and will be used to deserialize the URI query
///
/// This macro will generate a module that contains a `call()` function mirroring
/// the original function, and you may rely on this behavior (for example, for testing).
///
/// ```ignore
/// mod hello {
///    use super::*;
///    /// ... some internals hidden ...
///    pub(super) async fn call(_: &App) -> Result<Response<Body>, Error> {
///        Ok(Response::builder()
///            .status(StatusCode::OK)
///            .body("Hello, world".into())
///            .unwrap())
///    }
/// }
/// ```
#[proc_macro_attribute]
pub fn handler(meta: TokenStream, item: TokenStream) -> TokenStream {
    let ast = parse_macro_input!(item as syn::ItemFn);
    let methods = parse_macro_input!(meta as route::HandlerMethods).methods;
    route::handler(&methods, ast)
}

#[proc_macro_attribute]
pub fn scope(_: TokenStream, item: TokenStream) -> TokenStream {
    let ast = parse_macro_input!(item as syn::ItemFn);
    route::scope(ast)
}

#[proc_macro]
pub fn route(item: TokenStream) -> TokenStream {
    let mut ast = parse_macro_input!(item as syn::ExprMatch);
    route::route(&mut ast);
    quote!(#ast).into()
}

#[proc_macro_derive(ToField, attributes(option))]
pub fn derive_to_field(item: TokenStream) -> TokenStream {
    let ast = parse_macro_input!(item as syn::DeriveInput);
    TokenStream::from(forms::to_field(ast))
}