1extern crate proc_macro;
2
3use darling::FromMeta;
4use proc_macro::TokenStream;
5use quote::quote;
6
7#[proc_macro_derive(
8 Observe,
9 attributes(computed, reaction, autorun, fut_autorun, fut_reaction)
10)]
11pub fn observe_derive(_original: TokenStream) -> TokenStream {
12 TokenStream::new()
13}
14
15#[derive(Default, FromMeta)]
16struct FutureAttr {}
17
18#[proc_macro_attribute]
19pub fn store(_args: TokenStream, original: TokenStream) -> TokenStream {
20 let mut original = syn::parse_macro_input!(original as syn::ItemStruct);
21
22 original
23 .attrs
24 .push(syn::parse_quote!(#[derive(observe::Observe)]));
25
26 let name = original.ident.clone();
27 let mut computed = vec![];
28 let mut reaction = vec![];
29 let mut future = vec![];
30 for f in &original.fields {
31 for a in &f.attrs {
32 if a.path.get_ident().map(|i| i.to_string()) == Some(String::from("computed")) {
33 match &f.ident {
34 Some(ident) => computed.push(ident.clone()),
35 None => {}
36 }
37 }
38 if a.path.get_ident().map(|i| i.to_string()) == Some(String::from("autorun")) {
39 match &f.ident {
40 Some(ident) => reaction.push(ident.clone()),
41 None => {}
42 }
43 }
44 if a.path.get_ident().map(|i| i.to_string()) == Some(String::from("fut_autorun")) {
45 let args = a.tokens.clone().into();
46 let attr_args = syn::parse_macro_input!(args as syn::AttributeArgs);
47 let _args = FutureAttr::from_list(&attr_args);
48 match &f.ident {
49 Some(ident) => future.push(ident.clone()),
50 None => {}
51 }
52 }
53 }
54 }
55
56 let computed = computed.iter().map(|c| {
57 quote! {{
59 let this = std::rc::Rc::downgrade(&self);
60 self.#c.become_computed({
61 move |ctx| {
62 let arc = this.upgrade();
63 match arc {
64 Some(store) => {
65 store.#c(ctx)
66 },
67 None => {
68 panic!("Call on a dropped store")
69 }
70 }
71 }
72 });
73 }}
74 });
75
76 let reaction = reaction.iter().map(|r| {
77 quote! {{
79 let this = std::rc::Rc::downgrade(&self);
80 self.#r.become_autorun({
81 move |ctx| {
82 let arc = this.upgrade();
83 match arc {
84 Some(store) => {
85 store.#r(ctx)
86 },
87 None => {
88 panic!("Call on a dropped store")
89 }
90 }
91 }
92 });
93
94 self.#r.update();
95 }}
96 });
97
98 let future = future.iter().map(|r| {
99 quote! {{
101 let this = std::rc::Rc::downgrade(&self);
102 self.#r.become_fut_autorun(Box::new({
103 move |ctx| {
104 let arc = this.upgrade();
105 match arc {
106 Some(store) => {
107 store.#r(ctx)
108 },
109 None => {
110 panic!("Call on a dropped store")
111 }
112 }
113 }
114 }));
115
116 self.#r.update();
117 }}
118 });
119
120 let (impl_generics, ty_generics, where_clause) = original.generics.split_for_impl();
121
122 let output = quote! {
123 #original
124 impl #impl_generics #name #ty_generics #where_clause {
125 fn __init_observables(self: &std::rc::Rc<Self>) {
126 #(#computed)*
127 #(#reaction)*
128 #(#future)*
129 }
130 }
131 };
132
133 proc_macro::TokenStream::from(output)
134}
135
136#[proc_macro_attribute]
137pub fn create(_args: TokenStream, original: TokenStream) -> TokenStream {
138 let mut original = syn::parse_macro_input!(original as syn::ImplItemMethod);
139 let temp_ident = syn::Ident::new(
140 &format!("__private_{}", original.sig.ident),
141 original.sig.ident.span(),
142 );
143
144 let mut new_fn = original.clone();
145
146 let mut arg_pat = Vec::new();
147 for input in original.sig.inputs.iter() {
148 match input {
149 syn::FnArg::Typed(syn::PatType { pat, .. }) => {
150 arg_pat.push(quote!(#pat));
151 }
152 _ => {}
153 }
154 }
155
156 let block = proc_macro::TokenStream::from(quote! {
157 {
158 let this = Self::#temp_ident(
159 #(#arg_pat),*
160 );
161 this.__init_observables();
162 this
163 }
164 });
165
166 new_fn.block = syn::parse_macro_input!(block as syn::Block);
167
168 original.sig.ident = temp_ident;
169 original.vis = syn::Visibility::Inherited;
170
171 let output = quote! {
172 #original
173 #new_fn
174 };
175
176 proc_macro::TokenStream::from(output)
177}