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
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
#![cfg_attr(feature = "doc", feature(external_doc))]
#![doc(html_logo_url = "https://reign.rs/images/media/reign.png")]
#![doc(html_root_url = "https://docs.rs/reign_derive/0.2.1")]
#![cfg_attr(feature = "doc", doc(include = "../README.md"))]

use proc_macro::TokenStream;
use proc_macro_error::proc_macro_error;
use syn::parse_macro_input;

#[cfg(feature = "router-backend")]
mod router;
#[cfg(feature = "view")]
mod views;

mod utils;

pub(crate) const INTERNAL_ERR: &str =
    "Internal error on reign_derive. Please create an issue on https://github.com/pksunkara/reign";

/// Auto load the views from the given directory.
///
/// Folder names should start with an alphabet and end with alphanumeric
/// with underscores being allowed in the middle.
///
/// File names should start with an alphabet and end with alphanumeric
/// with underscores being allowed in the middle. The only allowed
/// extension is `.html`.
///
/// Ignores the other files and folders which do not adhere the above rules.
///
/// Both the folder and file names are converted to lower case before
/// building the template.
///
/// # Examples
///
/// ```ignore
/// use reign::prelude::*;
///
/// views!("src", "views");
/// ```
#[cfg(feature = "view")]
#[proc_macro]
pub fn views(input: TokenStream) -> TokenStream {
    let input: views::render::Views = parse_macro_input!(input);

    views::render::views(input).into()
}

/// Shorthand notation for rendering a view.
///
/// # Examples
///
/// Render the given view
///
/// ```ignore
/// use reign::prelude::*;
///
/// render!(pages::home)
/// ```
///
/// You can also specify a status code
///
/// ```ignore
/// use reign::prelude::*;
///
/// render!(pages::home, status = 201)
/// ```
#[cfg(feature = "view")]
#[proc_macro]
#[proc_macro_error]
pub fn render(input: TokenStream) -> TokenStream {
    let input: views::render::Render = parse_macro_input!(input);

    views::render::render(input).into()
}

/// Shorthand notation for returning a json response.
///
/// # Examples
///
/// Serialize into JSON and send the given value
///
/// ```ignore
/// use reign::prelude::*;
///
/// // User implements serde::Serialize
/// let user = User {
///     name: "John"
/// };
///
/// json!(user)
/// ```
///
/// You can also specify a status code
///
/// ```ignore
/// use reign::prelude::*;
///
/// json!(user, status = 201)
/// ```
#[cfg(feature = "view-backend")]
#[proc_macro]
pub fn json(input: TokenStream) -> TokenStream {
    let input: views::json::Json = parse_macro_input!(input);

    views::json::json(input).into()
}

/// Helper for defining a [reign_router](https://docs.rs/reign_router) handler.
///
/// # Examples
///
/// ```ignore
/// use anyhow::Error;
/// use reign::{
///     prelude::*,
///     router::{Request, Response}
/// };
///
/// #[action]
/// async fn name(req: &mut Request, id: String) -> Result<impl Response, Error> {
///     Ok(id)
/// }
/// ```
#[cfg(feature = "router-backend")]
#[proc_macro_attribute]
#[proc_macro_error]
pub fn action(_: TokenStream, input: TokenStream) -> TokenStream {
    let input: syn::ItemFn = parse_macro_input!(input);

    router::action::action(input).into()
}

/// Helper for defining a [reign_router](https://docs.rs/reign_router) Path.
///
/// # Examples
///
/// ```ignore
/// use reign::{
///     prelude::*,
///     router::{Router}
/// };
///
/// fn router(r: &mut Router) {
///     // Required param
///     r.get(p!("foo" / id / "bar"), foobar);
///
///     // Optional param
///     r.get(p!("foo" / id?), foobar);
///
///     // Regex param
///     r.get(p!("number" / id @ "[0-9]+"), number);
///
///     // Optional Regex param
///     r.get(p!("number" / id? @ "[0-9]+"), number);
///
///     // Glob param
///     r.get(p!("tree" / id*), tree);
///
///     // Optional Glob param
///     r.get(p!("tree" / id*?), tree);
/// }
/// ```
#[cfg(feature = "router-backend")]
#[proc_macro]
#[proc_macro_error]
pub fn p(input: TokenStream) -> TokenStream {
    let input: router::path::Path = parse_macro_input!(input);

    router::path::path(input).into()
}