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

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

mod cookies;
mod forms;
mod models;
mod route;
mod util;

#[proc_macro_attribute]
pub fn cookie(_: TokenStream, item: TokenStream) -> TokenStream {
    let ast = parse_macro_input!(item as syn::ItemStruct);
    let cookie = cookies::cookie(&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))
}

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

#[proc_macro_attribute]
pub fn model_type(_: TokenStream, item: TokenStream) -> TokenStream {
    let mut ast = parse_macro_input!(item as syn::Item);
    let impls = models::model_type(&mut ast);
    let mut tokens = ast.to_token_stream();
    tokens.extend(impls);
    TokenStream::from(tokens)
}