reactive_graph_reactive_derive/
lib.rs

1extern crate proc_macro;
2
3use proc_macro::TokenStream;
4
5use quote::ToTokens;
6use quote::TokenStreamExt;
7use quote::format_ident;
8use quote::quote;
9use syn::DeriveInput;
10use syn::Ident;
11use syn::PathArguments;
12use syn::Type;
13use syn::parse_macro_input;
14
15use darling::Error;
16use darling::FromDeriveInput;
17use darling::FromMeta;
18use darling::ast::NestedMeta;
19use proc_macro2::Span;
20
21#[derive(FromMeta)]
22struct ReactiveEntityConfig {
23    pub namespace: String,
24    pub type_name: String,
25}
26
27#[proc_macro_attribute]
28pub fn reactive_entity(args: TokenStream, item: TokenStream) -> TokenStream {
29    let input: DeriveInput = parse_macro_input!(item);
30    let attr_args = match NestedMeta::parse_meta_list(args.into()) {
31        Ok(attr_args) => attr_args,
32        Err(e) => {
33            return TokenStream::from(Error::from(e).write_errors());
34        }
35    };
36    let reactive_entity_config = match ReactiveEntityConfig::from_list(&attr_args) {
37        Ok(v) => v,
38        Err(e) => {
39            return TokenStream::from(e.write_errors());
40        }
41    };
42
43    let ident = input.ident.clone();
44    let namespace = reactive_entity_config.namespace;
45    let type_name = reactive_entity_config.type_name;
46
47    let output = match input.clone().data {
48        syn::Data::Struct(s) => match s.fields {
49            syn::Fields::Named(mut fields) => fields
50                .named
51                .iter_mut()
52                .map(|field| {
53                    let field_name = &field.ident.clone().unwrap();
54                    let field_type = &field.ty.clone();
55                    let field_vis = &field.vis;
56                    quote! {
57                        #field_vis #field_name: reactive_graph_reactive_service_api::TypedReactivePropertyImpl<uuid::Uuid, reactive_graph_reactive_model_impl::ReactiveEntity, #field_type>,
58                    }
59                })
60                .collect(),
61            _ => quote!(),
62        },
63        _ => quote!(),
64    };
65
66    let expanded = quote! {
67        #[derive(reactive_graph_reactive_service_api::ReactiveEntity)]
68        #[reactive_entity_derive(namespace = #namespace, type_name = #type_name)]
69        pub struct #ident {
70            // reactive_instance: reactive_graph_reactive_service_api::ReactiveEntity,
71            #output
72        }
73    }
74    .to_token_stream();
75    TokenStream::from(expanded)
76}
77
78#[derive(FromDeriveInput)]
79#[darling(attributes(reactive_entity_derive))]
80struct ReactiveEntityDeriveConfig {
81    pub namespace: String,
82    pub type_name: String,
83}
84
85#[proc_macro_derive(ReactiveEntity, attributes(reactive_entity_derive))]
86pub fn reactive_entity_derive(input: TokenStream) -> TokenStream {
87    let input: DeriveInput = parse_macro_input!(input);
88
89    let ident: Ident = input.ident.clone();
90
91    let config = match ReactiveEntityDeriveConfig::from_derive_input(&input) {
92        Ok(config) => config,
93        Err(e) => {
94            return e.write_errors().into();
95        }
96    };
97    let ident_entity_type = format_ident!("ENTITY_TYPE_ID__{}__{}", config.namespace.to_uppercase(), config.type_name.to_uppercase());
98    let namespace = config.namespace;
99    let type_name = config.type_name;
100
101    let mut constructor_fields = quote! {};
102    let mut constructor_parameter_bounds = quote! {};
103    let mut constructor_parameters = quote! {};
104    let mut constructor_properties = quote! {};
105    if let syn::Data::Struct(s) = input.data {
106        if let syn::Fields::Named(fields) = s.fields {
107            constructor_fields.append_all(fields.named.iter().map(|f| {
108                let field_name = f.ident.clone().unwrap();
109                let property_name = field_name.to_string();
110                quote! {
111                    #field_name: reactive_graph_reactive_service_api::TypedReactivePropertyConstructor::new(reactive_instance.clone(), #property_name),
112                }
113            }));
114
115            constructor_parameter_bounds.append_all(fields.named.iter().map(|f| {
116                let parameter_bound = format_ident!("{}", f.ident.clone().unwrap().to_string().to_uppercase());
117                let field_ty = match f.ty.clone() {
118                    Type::Path(p) => {
119                        let target_types: Vec<_> = p
120                            .path
121                            .segments
122                            .iter()
123                            .filter(|seg| seg.ident == Ident::new("TypedReactivePropertyImpl", Span::call_site()))
124                            .cloned()
125                            .map(|seg| seg.arguments)
126                            .filter_map(|args| match args {
127                                PathArguments::AngleBracketed(args) => Some(args),
128                                _ => None,
129                            })
130                            .map(|type_parameters| type_parameters.args)
131                            .filter_map(|type_parameters| type_parameters.last().cloned())
132                            .collect();
133                        let target_type = target_types.first().cloned().unwrap();
134                        target_type.to_token_stream()
135                    }
136                    _ => {
137                        quote! {}
138                    }
139                };
140                quote! {
141                    #parameter_bound: Into<#field_ty>,
142                }
143            }));
144
145            constructor_parameters.append_all(fields.named.iter().map(|f| {
146                let field_name = f.ident.clone().unwrap();
147                let parameter_bound = format_ident!("{}", field_name.to_string().to_uppercase());
148                quote! {
149                    #field_name: #parameter_bound,
150                }
151            }));
152
153            constructor_properties.append_all(fields.named.iter().map(|f| {
154                let field_name = f.ident.clone().unwrap();
155                let property_name = field_name.to_string();
156                quote! {
157                    properties.insert(#property_name.to_string(), serde_json::json!(#field_name.into()));
158                    // properties.insert(#property_name.to_string(), serde_json::json!(reactive_graph_reactive_service_api::TypedReactivePropertyAccessor::get(&#field_name.into())));
159                }
160            }));
161        }
162    }
163
164    let expanded = quote! {
165
166        pub static #ident_entity_type: std::sync::LazyLock<reactive_graph_graph::EntityTypeId> = std::sync::LazyLock::new(|| { reactive_graph_graph::EntityTypeId::new_from_type(#namespace, #type_name)});
167
168        #[automatically_derived]
169        impl From<reactive_graph_reactive_model_impl::ReactiveEntity> for #ident {
170            fn from(reactive_instance: reactive_graph_reactive_model_impl::ReactiveEntity) -> Self {
171                Self {
172                    // reactive_instance: reactive_instance.clone(),
173                    #constructor_fields
174                }
175            }
176        }
177        #[automatically_derived]
178        impl From<&reactive_graph_reactive_model_impl::ReactiveEntity> for #ident {
179            fn from(reactive_instance: &reactive_graph_reactive_model_impl::ReactiveEntity) -> Self {
180                Self {
181                    // reactive_instance: reactive_instance.clone(),
182                    #constructor_fields
183                }
184            }
185        }
186
187        #[automatically_derived]
188        impl #ident {
189            pub fn new
190                <#constructor_parameter_bounds>
191            (
192                #constructor_parameters
193            ) -> Self {
194                let id = uuid::Uuid::new_v4();
195                let mut properties = reactive_graph_graph::PropertyInstances::new();
196                #constructor_properties
197                // properties.insert("value".to_string(), serde_json::json!(value.into()));
198                let properties = reactive_graph_reactive_model_impl::ReactiveProperties::new_with_id_from_properties(id, properties);
199                let ty = std::ops::Deref::deref(&#ident_entity_type).clone();
200                Self::from(reactive_graph_reactive_model_impl::ReactiveEntity::builder().ty(ty).id(id).properties(properties).build())
201            }
202        }
203
204        #[automatically_derived]
205        impl reactive_graph_reactive_service_api::TypedReactivePropertyContainer<reactive_graph_graph::EntityTypeId, reactive_graph_graph::EntityType> for #ident {
206            fn new_with_ty<TY: Into<reactive_graph_graph::EntityTypeId>>(ty: TY) -> Self {
207                #ident::from(reactive_graph_reactive_model_impl::ReactiveEntity::builder().ty(ty).id(uuid::Uuid::new_v4()).build())
208            }
209
210            fn new_from_type(entity_type: &reactive_graph_graph::EntityType) -> Self {
211                #ident::from(reactive_graph_reactive_model_impl::ReactiveEntity::builder_from_entity_type(&entity_type).build())
212            }
213        }
214
215        #[automatically_derived]
216        impl Default for #ident {
217            fn default() -> Self {
218                let ty = std::ops::Deref::deref(&#ident_entity_type).clone();
219                #ident::from(reactive_graph_reactive_model_impl::ReactiveEntity::builder().ty(ty).id(uuid::Uuid::new_v4()).build())
220            }
221        }
222
223        // #[automatically_derived]
224        // impl reactive_graph_reactive_service_api::ReactiveInstanceGetter<reactive_graph_reactive_model_impl::ReactiveEntity> for #ident {
225        //     fn get_reactive_instance() -> &reactive_graph_reactive_model_impl::ReactiveEntity {
226        //         &self.reactive_instance
227        //     }
228        // }
229    };
230    TokenStream::from(expanded)
231}
232
233// #[proc_macro_derive(Trigger)]
234// pub fn reactiveInstanceTrigger(input: TokenStream) -> TokenStream {
235//     let input: DeriveInput = parse_macro_input!(input);
236//     let expanded = quote! {
237//         impl Action for #ident {
238//             fn trigger(&self) {
239//                 self.reactive_instance
240//             }
241//         }
242//     }
243//     TokenStream::from(expanded)
244// }