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
#[cfg(feature = "api")]
mod api;
#[cfg(all(feature = "api", feature = "stream"))]
mod api_stream;
mod args;
mod route;
mod util;
#[cfg(feature = "ws")]
mod ws;

#[cfg(feature = "api")]
use args::ApiArgs;
use args::Args;

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

enum Method {
	Get,
	Post,
	Put,
	Delete,
	Head,
}

impl Method {
	pub fn as_str(&self) -> &'static str {
		match self {
			Self::Get => "GET",
			Self::Post => "POST",
			Self::Put => "PUT",
			Self::Delete => "DELETE",
			Self::Head => "HEAD",
		}
	}
}

#[allow(dead_code)]
enum TransformOutput {
	No,
	Json,
}

fn to_compile_error(error: syn::Error) -> TokenStream {
	let compile_error = error.to_compile_error();
	quote!(#compile_error).into()
}

macro_rules! attribute_route {
	($name:ident, $method:ident, No) => {
		attribute_route!(_; $name, $method, No,);
	};
	($name:ident, $method:ident, Json) => {
		attribute_route!(_; $name, $method, Json, #[cfg(feature = "json")]);
	};
	(_; $name:ident, $method:ident, $output:ident, $(#[$attr:meta])*) => {
		#[proc_macro_attribute]
		$(#[$attr])*
		pub fn $name(attrs: TokenStream, item: TokenStream) -> TokenStream {
			let args = parse_macro_input!(attrs as Args);
			let item = parse_macro_input!(item as ItemFn);

			let stream = route::expand(
				args,
				item,
				Method::$method,
				TransformOutput::$output
			);

			stream
				.map(|stream| stream.into())
				.unwrap_or_else(to_compile_error)
		}
	}
}

attribute_route!(get, Get, No);
attribute_route!(post, Post, No);
attribute_route!(put, Put, No);
attribute_route!(delete, Delete, No);
attribute_route!(head, Head, No);

attribute_route!(get_json, Get, Json);
attribute_route!(post_json, Post, Json);
attribute_route!(put_json, Put, Json);
attribute_route!(delete_json, Delete, Json);
attribute_route!(head_json, Head, Json);

#[proc_macro_attribute]
#[cfg(feature = "ws")]
pub fn ws(attrs: TokenStream, item: TokenStream) -> TokenStream {
	let args = parse_macro_input!(attrs as Args);
	let item = parse_macro_input!(item as ItemFn);

	let stream = ws::expand(args, item);

	stream
		.map(|stream| stream.into())
		.unwrap_or_else(to_compile_error)
}

#[proc_macro_attribute]
#[cfg(feature = "api")]
pub fn api(attrs: TokenStream, item: TokenStream) -> TokenStream {
	let args = parse_macro_input!(attrs as ApiArgs);
	let item = parse_macro_input!(item as ItemFn);

	let stream = api::expand(args, item);

	stream
		.map(|stream| stream.into())
		.unwrap_or_else(to_compile_error)
}

#[proc_macro_attribute]
#[cfg(all(feature = "api", feature = "stream"))]
pub fn api_stream(attrs: TokenStream, item: TokenStream) -> TokenStream {
	let args = parse_macro_input!(attrs as ApiArgs);
	let item = parse_macro_input!(item as ItemFn);

	let stream = api_stream::expand(args, item);

	stream
		.map(|stream| stream.into())
		.unwrap_or_else(to_compile_error)
}