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
// The `quote!` macro requires deep recursion.
#![recursion_limit = "512"]
#![allow(clippy::match_like_matches_macro)]

extern crate proc_macro;

use proc_macro::TokenStream as TokenStream1;
use syn::{parse_macro_input, AttributeArgs, Item, ItemImpl};

mod controller;
mod guard;
mod middleware;
mod openapi;
mod utils;

/// Saphir macro for auto trait implementation on controllers
///
/// The base macro attribule look like this : `#[controller]` and is to be put
/// on top of a Controller's method impl block
///
/// ```ignore
/// #use saphir::prelude::*;
/// #use saphir_macro::controller;
///
/// #struct ExampleController;
///
/// #[controller]
/// impl ExampleController {
///     // ....
/// }
/// ```
///
/// Different arguments can be passed to the controller macro:
/// - `name="<newName>"` will take place of the default controller name (by
///   default the controller name is the struct name, lowercase, with the
///   "controller keyword stripped"). the name will result as the basepath of
///   the controller.
/// - `version=<u16>` use for api version, the version will be added before the
///   name as the controller basepath
/// - `prefix="<prefix>"` add a prefix before the basepath and the version.
///
/// ##Example
///
/// ```ignore
/// use saphir::prelude::*;
/// use saphir_macro::controller;
///
/// struct ExampleController;
///
/// #[controller(name="test", version=1, prefix="api")]
/// impl ExampleController {
///     // ....
/// }
/// ```
///
/// This will result in the Example controller being routed to `/api/v1/test`
#[proc_macro_attribute]
pub fn controller(args: TokenStream1, input: TokenStream1) -> TokenStream1 {
    let args = parse_macro_input!(args as AttributeArgs);
    let input = parse_macro_input!(input as ItemImpl);

    let expanded = controller::expand_controller(args, input).unwrap_or_else(|e| e.to_compile_error());

    TokenStream1::from(expanded)
}

#[proc_macro_attribute]
pub fn middleware(_args: TokenStream1, input: TokenStream1) -> TokenStream1 {
    let input = parse_macro_input!(input as ItemImpl);

    let expanded = middleware::expand_middleware(input).unwrap_or_else(|e| e.to_compile_error());

    TokenStream1::from(expanded)
}

#[proc_macro_attribute]
pub fn guard(_args: TokenStream1, input: TokenStream1) -> TokenStream1 {
    let input = parse_macro_input!(input as ItemImpl);

    let expanded = guard::expand_guard(input).unwrap_or_else(|e| e.to_compile_error());

    TokenStream1::from(expanded)
}

/// Saphir OpenAPI macro which can be put on top of a struct or enum definition.
/// Allow specifying informations for the corresponding type when generating
/// OpenAPI documentation through saphir's CLI.
///
/// The syntax looks like this : `#[openapi(mime = "application/json")]`.
/// `mime` can either be a full mimetype, or one of the following keywords:
/// - json (application/json)
/// - form (application/x-www-form-urlencoded)
/// - any  (*/*)
#[proc_macro_attribute]
pub fn openapi(args: TokenStream1, input: TokenStream1) -> TokenStream1 {
    let args = parse_macro_input!(args as AttributeArgs);
    let parsed_input = parse_macro_input!(input as Item);

    let expanded = openapi::validate_openapi(args, parsed_input).unwrap_or_else(|e| e.to_compile_error());

    TokenStream1::from(expanded)
}