Skip to main content

forsyde_io_generator/
lib.rs

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        /// Use the function `try_view` to create a viewer for a vertex if it has the trait."
365        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            // Automatically generated code by forsyde-io-generator: DO NOT EDIT MANUALLY
483            #[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    // println!("{}", code);
563    let code_file: syn::File = syn::parse2(code).map_err(|e| e.to_string())?;
564    Ok(prettyplease::unparse(&code_file))
565}