observe_macro/
lib.rs

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        // let get = format_ident!("get_{}", c);
58        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        // let get = format_ident!("get_{}", c);
78        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        // let get = format_ident!("get_{}", c);
100        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}