reactive_graph_reactive_derive/
lib.rs1extern 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 #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 }
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 #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 #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 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 };
230 TokenStream::from(expanded)
231}
232
233