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
use convert_case::{Case, Casing};
use proc_macro2::{Ident, Span, TokenStream};
use quote::quote;
use syn::{parse_macro_input, ItemEnum};
#[proc_macro_derive(OnEvent, attributes(unimplemented))]
pub fn derive_on_event(item: proc_macro::TokenStream) -> proc_macro::TokenStream {
let input = parse_macro_input!(item as ItemEnum);
derive_on_event_enum(input)
.unwrap_or_else(|err| err.to_compile_error())
.into()
}
fn derive_on_event_enum(item: ItemEnum) -> syn::Result<TokenStream> {
let mut on_event_handlers = vec![];
let mut set_on_event_handlers = vec![];
let mut off_event_handlers = vec![];
let mut event_handlers = vec![];
let html = quote! {
let html = self.dyn_ref::<web_sys::HtmlElement>()?;
};
let init_callback = quote! {
#html
let callback = Some(callback.as_ref().unchecked_ref());
};
let callback = quote! {
callback: &Closure<dyn FnMut(web_sys::Event)>,
};
for variant in item.variants.iter().filter(|variant| {
!variant
.attrs
.iter()
.any(|attr| attr.path.is_ident("unimplemented"))
}) {
let ident = &variant.ident;
let name = ident.to_string().to_case(Case::Snake);
let get_ident = Ident::new(&name, Span::call_site());
let set_ident = Ident::new(&format!("set_{}", name), Span::call_site());
let name2 = ident.to_string().to_lowercase();
let set_ident2 = Ident::new(&format!("set_on{}", name2), Span::call_site());
let get_ident2 = Ident::new(&format!("on{}", name2), Span::call_site());
on_event_handlers.push(quote! {
Event::#ident => html.#get_ident2().ok_or(Error::EventNotHandled(Event::#ident)),
});
set_on_event_handlers.push(quote! {
Event::#ident => Ok(html.#set_ident2(callback)),
});
off_event_handlers.push(quote! {
Event::#ident => Ok(html.#set_ident2(None)),
});
event_handlers.push(quote! {
fn #get_ident(&self) -> Result<js_sys::Function, Error> {
#html
html.#get_ident2().ok_or(Error::EventNotHandled(Event::#ident))
}
pub fn #set_ident(&self, #callback) -> Result<(), Error> {
#init_callback
Ok(html.#set_ident2(callback))
}
});
}
Ok(quote! {
impl Element {
pub fn on(&self, event: Event) -> Result<js_sys::Function, Error> {
#html
match event {
#(#on_event_handlers)*
_ => Err(Error::EventNotImplemented(event)),
}
}
pub fn set_on(&self, event: Event, #callback) -> Result<(), Error> {
#init_callback
match event {
#(#set_on_event_handlers)*
_ => Err(Error::EventNotImplemented(event)),
}
}
pub fn set_off(&self, event: Event, #callback) -> Result<(), Error> {
#html
match event {
#(#off_event_handlers)*
_ => Err(Error::EventNotImplemented(event)),
}
}
#(#event_handlers)*
}
})
}