1use petgraph::visit::Dfs;
2use petgraph::Directed;
3use petgraph::Graph;
4use proc_macro2::{Ident, TokenStream};
5use quote::format_ident;
6use quote::quote;
7use std::collections::HashMap;
8
9use serde::{Deserialize, Serialize};
10
11#[derive(Debug, Clone, Serialize, Deserialize)]
12pub struct PortSpec {
13 pub name: String,
14 #[serde(alias = "htmlDescription")]
15 pub html_description: Option<String>,
16 #[serde(skip)]
17 pub vertex_trait: Option<VertexTraitSpec>,
18 #[serde(skip)]
19 pub edge_trait: Option<EdgeTraitSpec>,
20 #[serde(alias = "vertexTrait")]
21 pub vertex_trait_name: String,
22 #[serde(alias = "edgeTrait")]
23 pub edge_trait_name: Option<String>,
24 pub multiple: bool,
25 pub optional: bool,
26 pub incoming: bool,
27 pub outgoing: bool,
28}
29
30#[derive(Debug, Clone, Serialize, Deserialize)]
31#[serde(tag = "category")]
32pub enum PropertySpecType {
33 IntegerPropertyType { bits: u32, unsigned: bool },
34 RealPropertyType { bits: u32 },
35 BooleanPropertyType,
36 StringPropertyType,
37 ArrayPropertyType {
38 #[serde(alias = "valueType")]
39 value_type: Box<PropertySpecType>
40 },
41 MapPropertyType {
42 #[serde(alias = "keyType")]
43 key_type: Box<PropertySpecType>,
44 #[serde(alias = "valueType")]
45 value_type: Box<PropertySpecType>
46 },
47}
48
49#[derive(Debug, Clone, Serialize, Deserialize)]
50pub struct PropertySpec {
51 pub name: String,
52 #[serde(alias = "htmlDescription")]
53 pub html_description: Option<String>,
54 #[serde(alias = "initializationCode")]
55 pub initialization_code: HashMap<String, String>,
56 #[serde(alias = "propertyType")]
57 pub property_type: PropertySpecType,
58 #[serde(alias = "defaultValue")]
59 pub default_value: serde_json::Value,
60}
61
62#[derive(Debug, Clone, Serialize, Deserialize)]
63pub struct VertexTraitSpec {
64 #[serde(alias = "canonicalName")]
65 pub canonical_name: String,
66 #[serde(alias = "htmlDescription")]
67 pub html_description: Option<String>,
68 #[serde(skip)]
69 pub refined_traits: Vec<VertexTraitSpec>,
70 #[serde(alias = "refinedTraits")]
71 pub refined_traits_names: Vec<String>,
72 #[serde(alias = "requiredPorts")]
73 pub required_ports: HashMap<String, PortSpec>,
74 #[serde(alias = "requiredProperties")]
75 pub required_properties: HashMap<String, PropertySpec>
76}
77
78impl PartialEq<VertexTraitSpec> for VertexTraitSpec {
79 fn eq(&self, other: &Self) -> bool {
80 self.canonical_name == other.canonical_name
81 }
82}
83
84#[derive(Debug, Clone, Serialize, Deserialize)]
85pub struct EdgeTraitSpec {
86 #[serde(alias = "canonicalName")]
87 pub canonical_name: String,
88 #[serde(alias = "htmlDescription")]
89 pub html_description: Option<String>,
90 #[serde(skip)]
91 pub refined_traits: Vec<EdgeTraitSpec>,
92 #[serde(alias = "refinedTraits")]
93 pub refined_traits_names: Vec<String>,
94}
95
96#[derive(Debug, Clone, Serialize, Deserialize)]
97pub struct TraitHierarchySpec {
98 #[serde(alias = "canonicalName")]
99 pub canonical_name: String,
100 #[serde(alias = "vertexTraits")]
101 pub vertex_traits: HashMap<String, VertexTraitSpec>,
102 #[serde(alias = "edgeTraits")]
103 pub edge_traits: HashMap<String, EdgeTraitSpec>,
104}
105
106pub type VertexTraitHierachy = Graph<VertexTraitSpec, (), Directed>;
107
108pub type EdgeTraitHierachy = Graph<EdgeTraitSpec, (), Directed>;
109
110impl From<&TraitHierarchySpec> for VertexTraitHierachy {
111 fn from(value: &TraitHierarchySpec) -> Self {
112 let mut idx_to_node = HashMap::new();
113 let mut graph = Graph::new();
114 for (name, vt) in &value.vertex_traits {
115 let node = graph.add_node(vt.clone());
116 idx_to_node.insert(name, node);
117 }
118 for (name, vt) in &value.vertex_traits {
119 for rt in &vt.refined_traits_names {
120 let node = idx_to_node.get(name).unwrap();
121 let refined_node = idx_to_node.get(rt).unwrap();
122 graph.add_edge(*node, *refined_node, ());
123 }
124 }
125 graph
126 }
127}
128
129impl From<&TraitHierarchySpec> for EdgeTraitHierachy {
130 fn from(value: &TraitHierarchySpec) -> Self {
131 let mut idx_to_node = HashMap::new();
132 let mut graph = Graph::new();
133 for (name, vt) in &value.edge_traits {
134 let node = graph.add_node(vt.clone());
135 idx_to_node.insert(name, node);
136 }
137 for (name, vt) in &value.edge_traits {
138 for rt in &vt.refined_traits_names {
139 let node = idx_to_node.get(name).unwrap();
140 let refined_node = idx_to_node.get(rt).unwrap();
141 graph.add_edge(*node, *refined_node, ());
142 }
143 }
144 graph
145 }
146}
147
148
149fn from_canonical_name(canonical_name: &str) -> TokenStream {
150 let splitted = canonical_name.split("::").map(|x| format_ident!("{}", x));
151 quote! {
152 #(#splitted)::*
153 }
154}
155
156impl From<&PropertySpecType> for TokenStream {
157 fn from(value: &PropertySpecType) -> Self {
158 match value {
159 PropertySpecType::IntegerPropertyType { bits, unsigned } => {
160 if bits <= &8 {
161 if *unsigned { quote! { u8 } } else { quote! { i8 } }
162 } else if bits <= &16 {
163 if *unsigned { quote! { u16 } } else { quote! { i16 } }
164 } else if bits <= &32 {
165 if *unsigned { quote! { u32 } } else { quote! { i32 } }
166 } else if bits <= &64 {
167 if *unsigned { quote! { u64 } } else { quote! { i64 } }
168 } else {
169 if *unsigned { quote! { u128 } } else { quote! { i128 } }
170 }
171 },
172 PropertySpecType::RealPropertyType { bits } => {
173 if bits <= &32 {
174 quote! { f32 }
175 } else {
176 quote! { f64 }
177 }
178 },
179 PropertySpecType::BooleanPropertyType => {
180 quote! {
181 bool
182 }
183 },
184 PropertySpecType::StringPropertyType => {
185 quote! {
186 String
187 }
188 },
189 PropertySpecType::ArrayPropertyType { value_type } => {
190 let value_type = TokenStream::from(value_type.as_ref());
191 quote! {
192 Vec<#value_type>
193 }
194 },
195 PropertySpecType::MapPropertyType { key_type, value_type } => {
196 let key_type = TokenStream::from(key_type.as_ref());
197 let value_type = TokenStream::from(value_type.as_ref());
198 quote! {
199 std::collections::HashMap<#key_type, #value_type>
200 }
201 }
202 }
203 }
204}
205
206impl From<&PropertySpec> for TokenStream {
207 fn from(value: &PropertySpec) -> Self {
208 let simple_name = &value.name;
209 let func_name = format_ident!("get_{}", &value.name);
210 let return_type = TokenStream::from(&value.property_type);
211 quote! {
212 fn #func_name(&self) -> #return_type {
213 <#return_type>::try_from(
214 self.get_vertex_properties().get(#simple_name).expect(&format!("Property {} for vertex {} should exist but does not.", #simple_name, self.get_identifier()))
215 ).expect(&format!("Property {} for vertex {} is of a wrong type.", #simple_name, self.get_identifier()))
216 }
217 }
218 }
219}
220
221impl From<&PortSpec> for TokenStream {
222 fn from(value: &PortSpec) -> Self {
223 let simple_name = value.vertex_trait_name.split("::").last().unwrap_or(&value.vertex_trait_name);
224 let returned_viewer = format_ident!("{}Viewer", simple_name);
225 let port_name = &value.name;
226 let func_name = format_ident!("get_{}", &value.name);
227 let direction_ident = match (value.incoming, value.outgoing) {
228 (true, false) => from_canonical_name("petgraph::Direction::Incoming"),
229 (false, true) => from_canonical_name("petgraph::Direction::Outgoing"),
230 _ => from_canonical_name("petgraph::Undirected"),
231 };
232 let skip_if_wrong_edge = value.edge_trait_name.as_ref().map(|e| {
233 quote! {
234 if eref.weight().traits.iter().all(|t| t.get_name() != #e) {
235 continue;
236 }
237 }
238 }).unwrap_or(quote! { });
239 let skip_if_wrong_port = match (value.incoming, value.outgoing) {
240 (true, false) => quote! {
241 if eref.weight().target_port.as_ref().map(|x| x != #port_name).unwrap_or(false) {
242 continue;
243 }
244 },
245 (false, true) => quote! {
246 if eref.weight().source_port.as_ref().map(|x| x != #port_name).unwrap_or(false) {
247 continue;
248 }
249 },
250 (true, true) => quote! {
251 if eref.weight().source_port.as_ref().map(|x| x != #port_name).unwrap_or(false) || eref.weight().target_port.as_ref().map(|x| x != #port_name).unwrap_or(false) {
252 continue;
253 }
254 },
255 _ => quote! { }
256 };
257 let edge_node_based_on_spec = if value.incoming {
258 quote! { _src_idx }
259 } else {
260 quote! { _tgt_idx }
261 };
262 match value.multiple {
263 false => {
264 quote! {
265 fn #func_name(&self) -> Option<#returned_viewer> {
266 let sg = self.get_system_graph();
267 let this = self.get_vertex();
268 let self_idx = sg.node_weights().position(|x| x.identifier == this.identifier)?;
269 let self_vertex = petgraph::visit::NodeIndexable::from_index(sg, self_idx);
270 for eref in sg.edges_directed(self_vertex, #direction_ident) {
271 #skip_if_wrong_edge
272 #skip_if_wrong_port
273 let (_src_idx, _tgt_idx) = sg.edge_endpoints(eref.id())?;
274 let idx = #edge_node_based_on_spec;
275 let v = sg.node_weight(idx)?;
276 let viewer_opt = #returned_viewer::try_view(v, sg);
277 if viewer_opt.is_some() {
278 return viewer_opt;
279 }
280 }
281 None
282 }
283 }
284 },
285 true => {
286 quote! {
287 fn #func_name(&self) -> Vec<#returned_viewer> {
288 let mut viewers = Vec::new();
289 let sg = self.get_system_graph();
290 let this = self.get_vertex();
291 if let Some(self_idx) = sg.node_weights().position(|x| x.identifier == this.identifier) {
292 let self_vertex = petgraph::visit::NodeIndexable::from_index(sg, self_idx);
293 for eref in sg.edges_directed(self_vertex, #direction_ident) {
294 #skip_if_wrong_edge
295 #skip_if_wrong_port
296 if let Some((_src_idx, _tgt_idx)) = sg.edge_endpoints(eref.id()) {
297 let idx = #edge_node_based_on_spec;
298 if let Some(v) = sg.node_weight(idx) {
299 if let Some(viewer) = #returned_viewer::try_view(v, sg) {
300 viewers.push(viewer);
301 }
302 }
303 }
304
305 }
306 }
307 viewers
308 }
309 }
310 }
311 }
312 }
313}
314
315fn vertex_code_from_trait_hierarchy(value: &VertexTraitSpec, hierarchy: &VertexTraitHierachy) -> TokenStream {
316 let canonical_name = &value.canonical_name;
317 let simple_name = value.canonical_name.split("::").last().unwrap_or(&value.canonical_name);
318 let viewer_ident = format_ident!("{}Viewer", simple_name);
319 let trait_ident = format_ident!("Is{}", simple_name);
320 let mut all_refined: Vec<&VertexTraitSpec> = vec![];
321 let value_idx = hierarchy.node_indices().find(|idx| hierarchy[*idx].canonical_name == value.canonical_name).unwrap();
322 let mut dfs = Dfs::new(hierarchy, value_idx);
323 while let Some(nx) = dfs.next(&hierarchy) {
324 let value = &hierarchy[nx];
325 if !all_refined.contains(&value) {
326 all_refined.push(value);
327 }
328 }
329 all_refined.remove(0);
330 let refinements = all_refined.iter().map(|rt| {
331 let simple_name = rt.canonical_name.split("::").last().unwrap_or(&rt.canonical_name);
332 format_ident!("Is{}", simple_name)
333 });
334 let refinements_implementations = all_refined.iter().map(|rt| {
335 let simple_name = rt.canonical_name.split("::").last().unwrap_or(&rt.canonical_name);
336 let i = format_ident!("Is{}", simple_name);
337 quote! { impl<'view> #i for #viewer_ident<'view> {} }
338 }).chain(
339 std::iter::once(quote! { impl<'view> #trait_ident for #viewer_ident<'view> {} })
340 );
341 let ports_methods = value.required_ports.iter().map(|(_, port_spec)| {
342 TokenStream::from(port_spec)
343 });
344 let properties_methods = value.required_properties.iter().map(|(_, property_spec)| {
345 TokenStream::from(property_spec)
346 });
347 let html_documentation = value.html_description.as_ref().map(String::as_str).unwrap_or("")
348 .replace("@deprecated", "deprecated:")
349 .replace("<p>", "")
350 .replace("</p>", "");
351 let doc_lines = html_documentation.split("\n")
352 .map(|s| format!(" {}", s.trim()));
353 let trait_msg = format!(" This struct is the generated vertex viewer for the trait `{}`.", value.canonical_name);
354 quote! {
355
356 pub trait #trait_ident: forsyde_io_core::VertexViewer #(+ #refinements) * {
357 #(#ports_methods)*
358
359 #(#properties_methods)*
360 }
361
362 #(#[doc = #doc_lines])*
363 #[doc = #trait_msg]
364 pub struct #viewer_ident<'view> {
366 pub vertex: &'view forsyde_io_core::Vertex,
367 pub system_graph: &'view forsyde_io_core::SystemGraph
368 }
369
370 impl<'view, 'sg: 'view> #viewer_ident<'view> {
371 pub fn try_view(vertex: &'sg forsyde_io_core::Vertex, system_graph: &'sg forsyde_io_core::SystemGraph) -> Option<Self> {
372 if vertex.traits.iter().any(|t| t.get_name() == #canonical_name) {
373 let new_vertex_ref = vertex;
374 let new_system_graph_ref = system_graph;
375 Some(Self { vertex: new_vertex_ref, system_graph: new_system_graph_ref })
376 } else {
377 None
378 }
379 }
380
381 }
382
383 impl<'view> forsyde_io_core::VertexViewer for #viewer_ident<'view> {
384 fn get_vertex(&self) -> &forsyde_io_core::Vertex {
385 self.vertex
386 }
387 fn get_system_graph(&self) -> &forsyde_io_core::SystemGraph {
388 self.system_graph
389 }
390 }
391
392 #(#refinements_implementations)*
393
394 }
395}
396
397impl From<&TraitHierarchySpec> for TokenStream {
398 fn from(hierarchy: &TraitHierarchySpec) -> Self {
399 let hierarchy_module_ident = format_ident!("{}", hierarchy.canonical_name.split("::").last().unwrap_or(&hierarchy.canonical_name.as_str()));
400 let vertex_trait_idents: Vec<Ident> = hierarchy.vertex_traits.keys().flat_map(|s| s.split("::").last()).map(|s| format_ident!("{}Trait", s)).collect();
401 let edge_trait_idents: Vec<Ident> = hierarchy.edge_traits.keys().flat_map(|s| s.split("::").last()).map(|s| format_ident!("{}Trait", s)).collect();
402 let vtrait_match = hierarchy.vertex_traits.iter().map(|(canonical_name, _)| {
403 let simple_name = canonical_name.split("::").last().unwrap_or(&canonical_name);
404 let simple_ident = format_ident!("{}Trait", simple_name);
405 quote! {
406 crate::#hierarchy_module_ident::VertexTraits::#simple_ident => #canonical_name
407 }
408 });
409 let vtrait_from_str = hierarchy.vertex_traits.iter().map(|(canonical_name, _)| {
410 let simple_name = canonical_name.split("::").last().unwrap_or(&canonical_name);
411 let simple_ident = format_ident!("{}Trait", simple_name);
412 quote! {
413 #canonical_name => Some(std::sync::Arc::new(crate::#hierarchy_module_ident::VertexTraits::#simple_ident) as std::sync::Arc<dyn forsyde_io_core::Trait>)
414 }
415 });
416 let vtraits_hierarchy = VertexTraitHierachy::from(hierarchy);
417 let etraits_hierarchy = EdgeTraitHierachy::from(hierarchy);
418 let vtrait_refine_match = hierarchy.vertex_traits.iter().map(|(name, _)| {
419 let vt_idx = vtraits_hierarchy.node_indices().find(|idx| &vtraits_hierarchy[*idx].canonical_name == name).unwrap();
420 let mut dfs = Dfs::new(&vtraits_hierarchy, vt_idx);
421 let mut refined = vec![];
422 while let Some(nx) = dfs.next(&vtraits_hierarchy) {
423 let value = &vtraits_hierarchy[nx];
424 refined.push(&value.canonical_name);
425 }
426 let this_match = if refined.len() > 0 { quote! {
427 match other.get_name() {
428 #(#refined => true),*,
429 _ => false
430 }
431 }} else {
432 quote! {
433 false
434 }
435
436 };
437 quote! {
438 #name => #this_match
439 }
440 });
441 let etrait_refines_match = hierarchy.edge_traits.iter().map(|(name, _)| {
442 let et_idx = etraits_hierarchy.node_indices().find(|idx| &etraits_hierarchy[*idx].canonical_name == name).unwrap();
443 let mut dfs = Dfs::new(&etraits_hierarchy, et_idx);
444 let mut refined = vec![];
445 while let Some(nx) = dfs.next(&etraits_hierarchy) {
446 let value = &etraits_hierarchy[nx];
447 refined.push(&value.canonical_name);
448 }
449 let this_match = if refined.len() > 0 { quote! {
450 match other.get_name() {
451 #(#refined => true),*,
452 _ => false
453 }
454 }} else {
455 quote! {
456 false
457 }
458
459 };
460 quote! {
461 #name => #this_match
462 }
463 });
464 let etrait_match = hierarchy.edge_traits.iter().map(|(canonical_name,_)| {
465 let simple_name = canonical_name.split("::").last().unwrap_or(&canonical_name);
466 let simple_ident = format_ident!("{}Trait", simple_name);
467 quote! {
468 crate::#hierarchy_module_ident::EdgeTraits::#simple_ident => #canonical_name
469 }
470 });
471 let etrait_from_str = hierarchy.edge_traits.iter().map(|(canonical_name,_)| {
472 let simple_name = canonical_name.split("::").last().unwrap_or(&canonical_name);
473 let simple_ident = format_ident!("{}Trait", simple_name);
474 quote! {
475 #canonical_name => Some(std::sync::Arc::new(crate::#hierarchy_module_ident::EdgeTraits::#simple_ident) as std::sync::Arc<dyn forsyde_io_core::Trait>)
476 }
477 });
478 let vtraits_code = hierarchy.vertex_traits.iter().map(|(_,vtrait)| {
479 vertex_code_from_trait_hierarchy(vtrait, &vtraits_hierarchy)
480 });
481 let code = quote! {
482 #[allow(non_camel_case_types)]
484 #[allow(non_snake_case)]
485 pub mod #hierarchy_module_ident {
486 use petgraph;
487 use petgraph::visit::EdgeRef;
488 use forsyde_io_core;
489
490 #[allow(dead_code)]
491 enum VertexTraits {
492 #(#vertex_trait_idents),*
493 }
494
495 #[allow(dead_code)]
496 enum EdgeTraits {
497 #(#edge_trait_idents),*
498 }
499
500 impl forsyde_io_core::Trait for VertexTraits {
501 fn get_name(&self) -> &str {
502 match self {
503 #(#vtrait_match),*
504 }
505 }
506
507 fn refines(&self, other: &dyn forsyde_io_core::Trait) -> bool {
508 if self.get_name() == other.get_name() {
509 true
510 } else {
511 match self.get_name() {
512 #(#vtrait_refine_match),*,
513 _ => false
514 }
515 }
516 }
517
518 }
519
520 impl forsyde_io_core::Trait for EdgeTraits {
521 fn get_name(&self) -> &str {
522 match self {
523 #(#etrait_match),*
524 }
525 }
526
527 fn refines(&self, other: &dyn forsyde_io_core::Trait) -> bool {
528 if self.get_name() == other.get_name() {
529 true
530 } else {
531 match self.get_name() {
532 #(#etrait_refines_match),*,
533 _ => false
534 }
535 }
536 }
537
538 }
539
540 pub fn trait_from_str(s: &str) -> Option<std::sync::Arc<dyn forsyde_io_core::Trait>> {
541 match s {
542 #(#vtrait_from_str),*,
543 #(#etrait_from_str),*,
544 _ => None
545 }
546 }
547
548 #(#vtraits_code)*
549 }
550 };
551 code
552 }
553}
554
555pub fn from_json_trait_hierarchy_to_code(hierarchy_json: &str) -> Result<String, String> {
556 let hierarchy: TraitHierarchySpec = serde_json::from_str(hierarchy_json).map_err(|e| e.to_string())?;
557 generate_trait_hierarchy_code(&hierarchy)
558}
559
560pub fn generate_trait_hierarchy_code(hierarchy: &TraitHierarchySpec) -> Result<String, String> {
561 let code = TokenStream::from(hierarchy);
562 let code_file: syn::File = syn::parse2(code).map_err(|e| e.to_string())?;
564 Ok(prettyplease::unparse(&code_file))
565}