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
#![deny(missing_docs)]
extern crate proc_macro;
use proc_macro::TokenStream;
use quote::quote_spanned;
use syn::{spanned::Spanned, AttributeArgs, FnArg, ItemFn, Meta, NestedMeta};
fn is_http(args: &AttributeArgs) -> bool {
args.iter().any(|arg| match arg {
NestedMeta::Meta(Meta::Path(path)) => path.is_ident("http"),
_ => false,
})
}
fn is_http_invoke(args: &AttributeArgs) -> bool {
args.iter().any(|arg| match arg {
NestedMeta::Meta(Meta::Path(path)) => path.is_ident("http::invoke"),
_ => false,
})
}
#[proc_macro_attribute]
pub fn lambda(attr: TokenStream, item: TokenStream) -> TokenStream {
let input = syn::parse_macro_input!(item as ItemFn);
let args = syn::parse_macro_input!(attr as AttributeArgs);
let ret = &input.sig.output;
let name = &input.sig.ident;
let body = &input.block;
let attrs = &input.attrs;
let asyncness = &input.sig.asyncness;
let inputs = &input.sig.inputs;
if name != "main" {
let tokens = quote_spanned! { name.span() =>
compile_error!("only the main function can be tagged with #[lambda]");
};
return TokenStream::from(tokens);
}
if asyncness.is_none() {
let tokens = quote_spanned! { input.span() =>
compile_error!("the async keyword is missing from the function declaration");
};
return TokenStream::from(tokens);
}
let result = match inputs.len() {
2 => {
let event = match inputs.first().expect("expected event argument") {
FnArg::Typed(arg) => arg,
_ => {
let tokens = quote_spanned! { inputs.span() =>
compile_error!("fn main's first argument must be fully formed");
};
return TokenStream::from(tokens);
}
};
let event_name = &event.pat;
let event_type = &event.ty;
let context = match inputs.iter().nth(1).expect("expected context argument") {
FnArg::Typed(arg) => arg,
_ => {
let tokens = quote_spanned! { inputs.span() =>
compile_error!("fn main's second argument must be fully formed");
};
return TokenStream::from(tokens);
}
};
let context_name = &context.pat;
let context_type = &context.ty;
if is_http(&args) {
quote_spanned! { input.span() =>
#(#attrs)*
#asyncness fn main() {
async fn actual(#event_name: #event_type, #context_name: #context_type) #ret #body
let f = lamedh_http::handler(actual);
lamedh_http::lambda::run(f).await.unwrap();
}
}
} else if is_http_invoke(&args) {
quote_spanned! { input.span() =>
#(#attrs)*
#asyncness fn main() {
async fn actual(#event_name: #event_type, #context_name: #context_type) #ret #body
let f = lamedh_http::proxy_handler(actual);
lamedh_http::lambda::run(f).await.unwrap();
}
}
} else {
quote_spanned! { input.span() =>
#(#attrs)*
#asyncness fn main() {
async fn actual(#event_name: #event_type, #context_name: #context_type) #ret #body
let f = lamedh_runtime::handler_fn(actual);
lamedh_runtime::run(f).await.unwrap();
}
}
}
}
_ => {
let tokens = quote_spanned! { inputs.span() =>
compile_error!("The #[lambda] macro can expects two arguments: a triggered event and lambda context.");
};
return TokenStream::from(tokens);
}
};
result.into()
}