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
use proc_macro::TokenStream;
use quote::{quote, ToTokens};
use syn::{
fold::{self, Fold},
parse_macro_input, parse_quote, parse_str,
visit::{self, Visit},
Block, FnArg, ItemFn, PatType, ReturnType, Signature
};
struct ArgCollectorVisit(Vec<PatType>);
impl<'ast> Visit<'ast> for ArgCollectorVisit {
fn visit_pat_type(&mut self, pat: &'ast PatType) {
self.0.push(pat.to_owned());
}
}
#[derive(Default)]
struct ModifiedFold {
sig: Option<Signature>,
}
impl Fold for ModifiedFold {
fn fold_signature(&mut self, mut node: Signature) -> Signature {
self.sig = Some(node.clone());
node.output = ReturnType::Default;
let mut args_visit = ArgCollectorVisit(vec![]);
visit::visit_signature(&mut args_visit, &node);
let injection =
parse_str::<FnArg>("mut __error_events: ::bevy_fallible::bevy_ecs::ResMut<::bevy_fallible::bevy_app::Events<::bevy_fallible::SystemErrorEvent>>")
.ok()
.and_then(|arg| if let FnArg::Typed(pat) = arg { Some(pat) } else { None })
.unwrap();
let pats = [&[injection], args_visit.0.as_slice()].concat();
node.inputs = pats.into_iter().map(|pat| FnArg::from(pat)).collect();
node
}
fn fold_block(&mut self, node: Block) -> Block {
let sig = self.sig.as_ref().unwrap();
let ident = format!("{}", sig.ident);
let ret = match &sig.output {
ReturnType::Default => panic!("fallible system should return Result"),
ReturnType::Type(_, t) => t,
};
let body: proc_macro2::TokenStream =
node.stmts
.into_iter()
.fold(proc_macro2::TokenStream::new(), |mut tokens, stmt| {
stmt.to_tokens(&mut tokens);
tokens
});
let stmts = vec![
parse_quote! { let mut __impl = || -> #ret { #body }; },
parse_quote! {
match __impl() {
Ok(_) => (),
Err(err) => {
__error_events.send(::bevy_fallible::SystemErrorEvent { system_name: #ident, error: err.into() });
}
};
},
];
Block {
brace_token: node.brace_token,
stmts,
}
}
}
#[proc_macro_attribute]
pub fn fallible_system(_attrs: TokenStream, code: TokenStream) -> TokenStream {
let input = parse_macro_input!(code as ItemFn);
let modified = fold::fold_item_fn(&mut ModifiedFold::default(), input);
let gen = quote! { #modified };
gen.into()
}