diff --git a/plantuml/parser/docs/README.md b/plantuml/parser/docs/README.md index 8d882d03..af2149c9 100644 --- a/plantuml/parser/docs/README.md +++ b/plantuml/parser/docs/README.md @@ -81,13 +81,13 @@ ComponentA --> ComponentB : uses === Parse Tree === Rule::startuml -> "@startuml" Rule::statement -> "package \"Sample SEooC\" ..." - Rule::component - Rule::nested_component -> "package \"Sample SEooC\"" - Rule::default_component - Rule::component_type -> "package" - Rule::default_component_name -> "\"Sample SEooC\"" + Rule::element + Rule::nested_element -> "package \"Sample SEooC\"" + Rule::default_element + Rule::element_kind -> "package" + Rule::default_element_name -> "\"Sample SEooC\"" Rule::alias -> "as SampleSEooC" - Rule::component_style -> "#LightBlue" + Rule::element_style -> "#LightBlue" Rule::statement_block -> "{ ... }" Rule::relation -> "ComponentA --> ComponentB : uses" Rule::relation_object -> "ComponentA" diff --git a/plantuml/parser/integration_test/deployment_diagram/BUILD b/plantuml/parser/integration_test/deployment_diagram/BUILD new file mode 100644 index 00000000..b9ab3880 --- /dev/null +++ b/plantuml/parser/integration_test/deployment_diagram/BUILD @@ -0,0 +1,20 @@ +# ******************************************************************************* +# Copyright (c) 2026 Contributors to the Eclipse Foundation +# +# See the NOTICE file(s) distributed with this work for additional +# information regarding copyright ownership. +# +# This program and the accompanying materials are made available under the +# terms of the Apache License Version 2.0 which is available at +# https://www.apache.org/licenses/LICENSE-2.0 +# +# SPDX-License-Identifier: Apache-2.0 +# ******************************************************************************* +filegroup( + name = "deployment_diagram_files", + srcs = glob([ + "**/*.puml", + "**/*.json", + ]), + visibility = ["//visibility:public"], +) diff --git a/plantuml/parser/integration_test/deployment_diagram/arrows_link/arrows_link.puml b/plantuml/parser/integration_test/deployment_diagram/arrows_link/arrows_link.puml new file mode 100644 index 00000000..7dfd7de5 --- /dev/null +++ b/plantuml/parser/integration_test/deployment_diagram/arrows_link/arrows_link.puml @@ -0,0 +1,45 @@ +' ******************************************************************************* +' Copyright (c) 2026 Contributors to the Eclipse Foundation +' +' See the NOTICE file(s) distributed with this work for additional +' information regarding copyright ownership. +' +' This program and the accompanying materials are made available under the +' terms of the Apache License Version 2.0 which is available at +' https://www.apache.org/licenses/LICENSE-2.0 +' +' SPDX-License-Identifier: Apache-2.0 +' ******************************************************************************* +@startuml arrows_link + +node host_a +node host_b +node host_c +node host_d +node host_e +host_a -- host_b : mark_a +host_a .. host_c : mark_b +host_a ~~ host_d : mark_c +host_a == host_e + +artifact bundle_a +artifact bundle_b +artifact bundle_c +artifact bundle_d +artifact bundle_e +artifact bundle_f +artifact bundle_g +artifact bundle_h +artifact bundle_i +artifact bundle_j +bundle_a --> bundle_b +bundle_a --* bundle_c +bundle_a --o bundle_d +bundle_a --+ bundle_e +bundle_a --# bundle_f +bundle_a -->> bundle_g +bundle_a --0 bundle_h +bundle_a --^ bundle_i +bundle_a --(0 bundle_j + +@enduml diff --git a/plantuml/parser/integration_test/deployment_diagram/arrows_link/output.json b/plantuml/parser/integration_test/deployment_diagram/arrows_link/output.json new file mode 100644 index 00000000..8a03e70e --- /dev/null +++ b/plantuml/parser/integration_test/deployment_diagram/arrows_link/output.json @@ -0,0 +1,206 @@ +{ + "arrows_link.puml": { + "host_a": { + "id": "host_a", + "name": "host_a", + "alias": null, + "parent_id": null, + "comp_type": "Node", + "stereotype": null, + "relations": [ + { + "target": "host_b", + "annotation": "mark_a", + "relation_type": "None" + }, + { + "target": "host_c", + "annotation": "mark_b", + "relation_type": "None" + }, + { + "target": "host_d", + "annotation": "mark_c", + "relation_type": "None" + }, + { + "target": "host_e", + "annotation": null, + "relation_type": "None" + } + ] + }, + "host_b": { + "id": "host_b", + "name": "host_b", + "alias": null, + "parent_id": null, + "comp_type": "Node", + "stereotype": null, + "relations": [] + }, + "host_c": { + "id": "host_c", + "name": "host_c", + "alias": null, + "parent_id": null, + "comp_type": "Node", + "stereotype": null, + "relations": [] + }, + "host_d": { + "id": "host_d", + "name": "host_d", + "alias": null, + "parent_id": null, + "comp_type": "Node", + "stereotype": null, + "relations": [] + }, + "host_e": { + "id": "host_e", + "name": "host_e", + "alias": null, + "parent_id": null, + "comp_type": "Node", + "stereotype": null, + "relations": [] + }, + "bundle_a": { + "id": "bundle_a", + "name": "bundle_a", + "alias": null, + "parent_id": null, + "comp_type": "Artifact", + "stereotype": null, + "relations": [ + { + "target": "bundle_b", + "annotation": null, + "relation_type": "None" + }, + { + "target": "bundle_c", + "annotation": null, + "relation_type": "None" + }, + { + "target": "bundle_d", + "annotation": null, + "relation_type": "None" + }, + { + "target": "bundle_e", + "annotation": null, + "relation_type": "None" + }, + { + "target": "bundle_f", + "annotation": null, + "relation_type": "None" + }, + { + "target": "bundle_g", + "annotation": null, + "relation_type": "None" + }, + { + "target": "bundle_h", + "annotation": null, + "relation_type": "None" + }, + { + "target": "bundle_i", + "annotation": null, + "relation_type": "None" + }, + { + "target": "bundle_j", + "annotation": null, + "relation_type": "None" + } + ] + }, + "bundle_b": { + "id": "bundle_b", + "name": "bundle_b", + "alias": null, + "parent_id": null, + "comp_type": "Artifact", + "stereotype": null, + "relations": [] + }, + "bundle_c": { + "id": "bundle_c", + "name": "bundle_c", + "alias": null, + "parent_id": null, + "comp_type": "Artifact", + "stereotype": null, + "relations": [] + }, + "bundle_d": { + "id": "bundle_d", + "name": "bundle_d", + "alias": null, + "parent_id": null, + "comp_type": "Artifact", + "stereotype": null, + "relations": [] + }, + "bundle_e": { + "id": "bundle_e", + "name": "bundle_e", + "alias": null, + "parent_id": null, + "comp_type": "Artifact", + "stereotype": null, + "relations": [] + }, + "bundle_f": { + "id": "bundle_f", + "name": "bundle_f", + "alias": null, + "parent_id": null, + "comp_type": "Artifact", + "stereotype": null, + "relations": [] + }, + "bundle_g": { + "id": "bundle_g", + "name": "bundle_g", + "alias": null, + "parent_id": null, + "comp_type": "Artifact", + "stereotype": null, + "relations": [] + }, + "bundle_h": { + "id": "bundle_h", + "name": "bundle_h", + "alias": null, + "parent_id": null, + "comp_type": "Artifact", + "stereotype": null, + "relations": [] + }, + "bundle_i": { + "id": "bundle_i", + "name": "bundle_i", + "alias": null, + "parent_id": null, + "comp_type": "Artifact", + "stereotype": null, + "relations": [] + }, + "bundle_j": { + "id": "bundle_j", + "name": "bundle_j", + "alias": null, + "parent_id": null, + "comp_type": "Artifact", + "stereotype": null, + "relations": [] + } + } +} diff --git a/plantuml/parser/integration_test/deployment_diagram/declare_elements/declare_elements.puml b/plantuml/parser/integration_test/deployment_diagram/declare_elements/declare_elements.puml new file mode 100644 index 00000000..bbc01d7d --- /dev/null +++ b/plantuml/parser/integration_test/deployment_diagram/declare_elements/declare_elements.puml @@ -0,0 +1,38 @@ +' ******************************************************************************* +' Copyright (c) 2026 Contributors to the Eclipse Foundation +' +' See the NOTICE file(s) distributed with this work for additional +' information regarding copyright ownership. +' +' This program and the accompanying materials are made available under the +' terms of the Apache License Version 2.0 which is available at +' https://www.apache.org/licenses/LICENSE-2.0 +' +' SPDX-License-Identifier: Apache-2.0 +' ******************************************************************************* +@startuml declare_elements + +' Generic fixture names to avoid mirroring external example text. +actor role_a +agent role_b +artifact item_a +boundary zone_a +card item_b +cloud zone_b +component part_a +control part_b +database store_a +entity record_a +file doc_a +folder dir_a +frame zone_c +interface port_a +node host_a +package pack_a +queue lane_a +stack lane_b +rectangle box_a +storage store_b +usecase case_a + +@enduml diff --git a/plantuml/parser/integration_test/deployment_diagram/declare_elements/output.json b/plantuml/parser/integration_test/deployment_diagram/declare_elements/output.json new file mode 100644 index 00000000..34ea7b91 --- /dev/null +++ b/plantuml/parser/integration_test/deployment_diagram/declare_elements/output.json @@ -0,0 +1,193 @@ +{ + "declare_elements.puml": { + "role_a": { + "id": "role_a", + "name": "role_a", + "alias": null, + "parent_id": null, + "comp_type": "Actor", + "stereotype": null, + "relations": [] + }, + "role_b": { + "id": "role_b", + "name": "role_b", + "alias": null, + "parent_id": null, + "comp_type": "Agent", + "stereotype": null, + "relations": [] + }, + "item_a": { + "id": "item_a", + "name": "item_a", + "alias": null, + "parent_id": null, + "comp_type": "Artifact", + "stereotype": null, + "relations": [] + }, + "zone_a": { + "id": "zone_a", + "name": "zone_a", + "alias": null, + "parent_id": null, + "comp_type": "Boundary", + "stereotype": null, + "relations": [] + }, + "item_b": { + "id": "item_b", + "name": "item_b", + "alias": null, + "parent_id": null, + "comp_type": "Card", + "stereotype": null, + "relations": [] + }, + "zone_b": { + "id": "zone_b", + "name": "zone_b", + "alias": null, + "parent_id": null, + "comp_type": "Cloud", + "stereotype": null, + "relations": [] + }, + "part_a": { + "id": "part_a", + "name": "part_a", + "alias": null, + "parent_id": null, + "comp_type": "Component", + "stereotype": null, + "relations": [] + }, + "part_b": { + "id": "part_b", + "name": "part_b", + "alias": null, + "parent_id": null, + "comp_type": "Control", + "stereotype": null, + "relations": [] + }, + "store_a": { + "id": "store_a", + "name": "store_a", + "alias": null, + "parent_id": null, + "comp_type": "Database", + "stereotype": null, + "relations": [] + }, + "record_a": { + "id": "record_a", + "name": "record_a", + "alias": null, + "parent_id": null, + "comp_type": "Entity", + "stereotype": null, + "relations": [] + }, + "doc_a": { + "id": "doc_a", + "name": "doc_a", + "alias": null, + "parent_id": null, + "comp_type": "File", + "stereotype": null, + "relations": [] + }, + "dir_a": { + "id": "dir_a", + "name": "dir_a", + "alias": null, + "parent_id": null, + "comp_type": "Folder", + "stereotype": null, + "relations": [] + }, + "zone_c": { + "id": "zone_c", + "name": "zone_c", + "alias": null, + "parent_id": null, + "comp_type": "Frame", + "stereotype": null, + "relations": [] + }, + "port_a": { + "id": "port_a", + "name": "port_a", + "alias": null, + "parent_id": null, + "comp_type": "Interface", + "stereotype": null, + "relations": [] + }, + "host_a": { + "id": "host_a", + "name": "host_a", + "alias": null, + "parent_id": null, + "comp_type": "Node", + "stereotype": null, + "relations": [] + }, + "pack_a": { + "id": "pack_a", + "name": "pack_a", + "alias": null, + "parent_id": null, + "comp_type": "Package", + "stereotype": null, + "relations": [] + }, + "lane_a": { + "id": "lane_a", + "name": "lane_a", + "alias": null, + "parent_id": null, + "comp_type": "Queue", + "stereotype": null, + "relations": [] + }, + "lane_b": { + "id": "lane_b", + "name": "lane_b", + "alias": null, + "parent_id": null, + "comp_type": "Stack", + "stereotype": null, + "relations": [] + }, + "box_a": { + "id": "box_a", + "name": "box_a", + "alias": null, + "parent_id": null, + "comp_type": "Rectangle", + "stereotype": null, + "relations": [] + }, + "store_b": { + "id": "store_b", + "name": "store_b", + "alias": null, + "parent_id": null, + "comp_type": "Storage", + "stereotype": null, + "relations": [] + }, + "case_a": { + "id": "case_a", + "name": "case_a", + "alias": null, + "parent_id": null, + "comp_type": "Usecase", + "stereotype": null, + "relations": [] + } + } +} diff --git a/plantuml/parser/integration_test/deployment_diagram/deployment_diagram_it/deployment_diagram_it.puml b/plantuml/parser/integration_test/deployment_diagram/deployment_diagram_it/deployment_diagram_it.puml new file mode 100644 index 00000000..a2a492ca --- /dev/null +++ b/plantuml/parser/integration_test/deployment_diagram/deployment_diagram_it/deployment_diagram_it.puml @@ -0,0 +1,40 @@ +' ******************************************************************************* +' Copyright (c) 2026 Contributors to the Eclipse Foundation +' +' See the NOTICE file(s) distributed with this work for additional +' information regarding copyright ownership. +' +' This program and the accompanying materials are made available under the +' terms of the Apache License Version 2.0 which is available at +' https://www.apache.org/licenses/LICENSE-2.0 +' +' SPDX-License-Identifier: Apache-2.0 +' ******************************************************************************* +@startuml deployment_diagram + +package aaa{ + actor Driver + + node "ECU1 (Provider)" as ECU1 { + + boundary "SOME/IP Service Interface" as someip + + control SpeedService + + entity VehicleSpeed + + } + + node "ECU2 (Consumer)" as ECU2 { + + control DisplayService + + } + + Driver --> DisplayService + DisplayService --> someip + someip --> SpeedService + SpeedService --> VehicleSpeed +} + +@enduml diff --git a/plantuml/parser/integration_test/deployment_diagram/deployment_diagram_it/output.json b/plantuml/parser/integration_test/deployment_diagram/deployment_diagram_it/output.json new file mode 100644 index 00000000..74727ad7 --- /dev/null +++ b/plantuml/parser/integration_test/deployment_diagram/deployment_diagram_it/output.json @@ -0,0 +1,100 @@ +{ + "deployment_diagram_it.puml": { + "aaa": { + "id": "aaa", + "name": "aaa", + "alias": null, + "parent_id": null, + "comp_type": "Package", + "stereotype": null, + "relations": [] + }, + "aaa.Driver": { + "id": "aaa.Driver", + "name": "Driver", + "alias": null, + "parent_id": "aaa", + "comp_type": "Actor", + "stereotype": null, + "relations": [ + { + "target": "aaa.ECU2.DisplayService", + "annotation": null, + "relation_type": "None" + } + ] + }, + "aaa.ECU1": { + "id": "aaa.ECU1", + "name": "ECU1 (Provider)", + "alias": "ECU1", + "parent_id": "aaa", + "comp_type": "Node", + "stereotype": null, + "relations": [] + }, + "aaa.ECU1.someip": { + "id": "aaa.ECU1.someip", + "name": "SOME/IP Service Interface", + "alias": "someip", + "parent_id": "aaa.ECU1", + "comp_type": "Boundary", + "stereotype": null, + "relations": [ + { + "target": "aaa.ECU1.SpeedService", + "annotation": null, + "relation_type": "None" + } + ] + }, + "aaa.ECU1.SpeedService": { + "id": "aaa.ECU1.SpeedService", + "name": "SpeedService", + "alias": null, + "parent_id": "aaa.ECU1", + "comp_type": "Control", + "stereotype": null, + "relations": [ + { + "target": "aaa.ECU1.VehicleSpeed", + "annotation": null, + "relation_type": "None" + } + ] + }, + "aaa.ECU1.VehicleSpeed": { + "id": "aaa.ECU1.VehicleSpeed", + "name": "VehicleSpeed", + "alias": null, + "parent_id": "aaa.ECU1", + "comp_type": "Entity", + "stereotype": null, + "relations": [] + }, + "aaa.ECU2": { + "id": "aaa.ECU2", + "name": "ECU2 (Consumer)", + "alias": "ECU2", + "parent_id": "aaa", + "comp_type": "Node", + "stereotype": null, + "relations": [] + }, + "aaa.ECU2.DisplayService": { + "id": "aaa.ECU2.DisplayService", + "name": "DisplayService", + "alias": null, + "parent_id": "aaa.ECU2", + "comp_type": "Control", + "stereotype": null, + "relations": [ + { + "target": "aaa.ECU1.someip", + "annotation": null, + "relation_type": "None" + } + ] + } + } +} diff --git a/plantuml/parser/integration_test/deployment_diagram/nested_elements/nested_elements.puml b/plantuml/parser/integration_test/deployment_diagram/nested_elements/nested_elements.puml new file mode 100644 index 00000000..c0674c50 --- /dev/null +++ b/plantuml/parser/integration_test/deployment_diagram/nested_elements/nested_elements.puml @@ -0,0 +1,46 @@ +' ******************************************************************************* +' Copyright (c) 2026 Contributors to the Eclipse Foundation +' +' See the NOTICE file(s) distributed with this work for additional +' information regarding copyright ownership. +' +' This program and the accompanying materials are made available under the +' terms of the Apache License Version 2.0 which is available at +' https://www.apache.org/licenses/LICENSE-2.0 +' +' SPDX-License-Identifier: Apache-2.0 +' ******************************************************************************* +@startuml nested_elements + +artifact item_a { + card item_b { + cloud zone_a { + component part_a { + database store_a { + file doc_a { + folder dir_a { + frame zone_b { + hexagon cell_a { + node host_a { + package pack_a { + queue lane_a { + rectangle box_a { + stack lane_b { + storage store_b { + } + } + } + } + } + } + } + } + } + } + } + } + } + } +} + +@enduml diff --git a/plantuml/parser/integration_test/deployment_diagram/nested_elements/output.json b/plantuml/parser/integration_test/deployment_diagram/nested_elements/output.json new file mode 100644 index 00000000..e6cda8f6 --- /dev/null +++ b/plantuml/parser/integration_test/deployment_diagram/nested_elements/output.json @@ -0,0 +1,139 @@ +{ + "nested_elements.puml": { + "item_a": { + "id": "item_a", + "name": "item_a", + "alias": null, + "parent_id": null, + "comp_type": "Artifact", + "stereotype": null, + "relations": [] + }, + "item_a.item_b": { + "id": "item_a.item_b", + "name": "item_b", + "alias": null, + "parent_id": "item_a", + "comp_type": "Card", + "stereotype": null, + "relations": [] + }, + "item_a.item_b.zone_a": { + "id": "item_a.item_b.zone_a", + "name": "zone_a", + "alias": null, + "parent_id": "item_a.item_b", + "comp_type": "Cloud", + "stereotype": null, + "relations": [] + }, + "item_a.item_b.zone_a.part_a": { + "id": "item_a.item_b.zone_a.part_a", + "name": "part_a", + "alias": null, + "parent_id": "item_a.item_b.zone_a", + "comp_type": "Component", + "stereotype": null, + "relations": [] + }, + "item_a.item_b.zone_a.part_a.store_a": { + "id": "item_a.item_b.zone_a.part_a.store_a", + "name": "store_a", + "alias": null, + "parent_id": "item_a.item_b.zone_a.part_a", + "comp_type": "Database", + "stereotype": null, + "relations": [] + }, + "item_a.item_b.zone_a.part_a.store_a.doc_a": { + "id": "item_a.item_b.zone_a.part_a.store_a.doc_a", + "name": "doc_a", + "alias": null, + "parent_id": "item_a.item_b.zone_a.part_a.store_a", + "comp_type": "File", + "stereotype": null, + "relations": [] + }, + "item_a.item_b.zone_a.part_a.store_a.doc_a.dir_a": { + "id": "item_a.item_b.zone_a.part_a.store_a.doc_a.dir_a", + "name": "dir_a", + "alias": null, + "parent_id": "item_a.item_b.zone_a.part_a.store_a.doc_a", + "comp_type": "Folder", + "stereotype": null, + "relations": [] + }, + "item_a.item_b.zone_a.part_a.store_a.doc_a.dir_a.zone_b": { + "id": "item_a.item_b.zone_a.part_a.store_a.doc_a.dir_a.zone_b", + "name": "zone_b", + "alias": null, + "parent_id": "item_a.item_b.zone_a.part_a.store_a.doc_a.dir_a", + "comp_type": "Frame", + "stereotype": null, + "relations": [] + }, + "item_a.item_b.zone_a.part_a.store_a.doc_a.dir_a.zone_b.cell_a": { + "id": "item_a.item_b.zone_a.part_a.store_a.doc_a.dir_a.zone_b.cell_a", + "name": "cell_a", + "alias": null, + "parent_id": "item_a.item_b.zone_a.part_a.store_a.doc_a.dir_a.zone_b", + "comp_type": "Hexagon", + "stereotype": null, + "relations": [] + }, + "item_a.item_b.zone_a.part_a.store_a.doc_a.dir_a.zone_b.cell_a.host_a": { + "id": "item_a.item_b.zone_a.part_a.store_a.doc_a.dir_a.zone_b.cell_a.host_a", + "name": "host_a", + "alias": null, + "parent_id": "item_a.item_b.zone_a.part_a.store_a.doc_a.dir_a.zone_b.cell_a", + "comp_type": "Node", + "stereotype": null, + "relations": [] + }, + "item_a.item_b.zone_a.part_a.store_a.doc_a.dir_a.zone_b.cell_a.host_a.pack_a": { + "id": "item_a.item_b.zone_a.part_a.store_a.doc_a.dir_a.zone_b.cell_a.host_a.pack_a", + "name": "pack_a", + "alias": null, + "parent_id": "item_a.item_b.zone_a.part_a.store_a.doc_a.dir_a.zone_b.cell_a.host_a", + "comp_type": "Package", + "stereotype": null, + "relations": [] + }, + "item_a.item_b.zone_a.part_a.store_a.doc_a.dir_a.zone_b.cell_a.host_a.pack_a.lane_a": { + "id": "item_a.item_b.zone_a.part_a.store_a.doc_a.dir_a.zone_b.cell_a.host_a.pack_a.lane_a", + "name": "lane_a", + "alias": null, + "parent_id": "item_a.item_b.zone_a.part_a.store_a.doc_a.dir_a.zone_b.cell_a.host_a.pack_a", + "comp_type": "Queue", + "stereotype": null, + "relations": [] + }, + "item_a.item_b.zone_a.part_a.store_a.doc_a.dir_a.zone_b.cell_a.host_a.pack_a.lane_a.box_a": { + "id": "item_a.item_b.zone_a.part_a.store_a.doc_a.dir_a.zone_b.cell_a.host_a.pack_a.lane_a.box_a", + "name": "box_a", + "alias": null, + "parent_id": "item_a.item_b.zone_a.part_a.store_a.doc_a.dir_a.zone_b.cell_a.host_a.pack_a.lane_a", + "comp_type": "Rectangle", + "stereotype": null, + "relations": [] + }, + "item_a.item_b.zone_a.part_a.store_a.doc_a.dir_a.zone_b.cell_a.host_a.pack_a.lane_a.box_a.lane_b": { + "id": "item_a.item_b.zone_a.part_a.store_a.doc_a.dir_a.zone_b.cell_a.host_a.pack_a.lane_a.box_a.lane_b", + "name": "lane_b", + "alias": null, + "parent_id": "item_a.item_b.zone_a.part_a.store_a.doc_a.dir_a.zone_b.cell_a.host_a.pack_a.lane_a.box_a", + "comp_type": "Stack", + "stereotype": null, + "relations": [] + }, + "item_a.item_b.zone_a.part_a.store_a.doc_a.dir_a.zone_b.cell_a.host_a.pack_a.lane_a.box_a.lane_b.store_b": { + "id": "item_a.item_b.zone_a.part_a.store_a.doc_a.dir_a.zone_b.cell_a.host_a.pack_a.lane_a.box_a.lane_b.store_b", + "name": "store_b", + "alias": null, + "parent_id": "item_a.item_b.zone_a.part_a.store_a.doc_a.dir_a.zone_b.cell_a.host_a.pack_a.lane_a.box_a.lane_b", + "comp_type": "Storage", + "stereotype": null, + "relations": [] + } + } +} diff --git a/plantuml/parser/integration_test/src/test_error_view.rs b/plantuml/parser/integration_test/src/test_error_view.rs index 804f63c1..f6427729 100644 --- a/plantuml/parser/integration_test/src/test_error_view.rs +++ b/plantuml/parser/integration_test/src/test_error_view.rs @@ -17,7 +17,7 @@ use puml_parser::{ BaseParseError, ClassError, IncludeExpandError, IncludeParseError, PreprocessError, ProcedureExpandError, ProcedureParseError, }; -use puml_resolver::{ClassPumlResolverError, ComponentResolverError}; +use puml_resolver::{ClassPumlResolverError, ElementResolverError}; #[derive(Debug)] pub struct ProjectedError { @@ -197,22 +197,22 @@ impl ErrorView for ClassError { } } -impl ErrorView for ComponentResolverError { +impl ErrorView for ElementResolverError { fn project(&self, _base_dir: &Path) -> ProjectedError { match self { - ComponentResolverError::UnresolvedReference { reference } => { + ElementResolverError::UnresolvedReference { reference } => { ProjectedError::new("UnresolvedReference") .with_field("reference", reference.clone()) } - ComponentResolverError::DuplicateComponent { component_id } => { + ElementResolverError::DuplicateElement { element_id } => { ProjectedError::new("DuplicateComponent") - .with_field("component_id", component_id.clone()) + .with_field("component_id", element_id.clone()) } - ComponentResolverError::UnknownComponentType { component_type } => { + ElementResolverError::UnknownElementType { element_type } => { ProjectedError::new("UnknownComponentType") - .with_field("component_type", component_type.clone()) + .with_field("component_type", element_type.clone()) } } } diff --git a/plantuml/parser/puml_cli/src/main.rs b/plantuml/parser/puml_cli/src/main.rs index 9d6b3e60..47c72b89 100644 --- a/plantuml/parser/puml_cli/src/main.rs +++ b/plantuml/parser/puml_cli/src/main.rs @@ -27,7 +27,7 @@ use puml_parser::{ PumlSequenceParser, }; use puml_resolver::{ - ClassResolver, ComponentResolver, DiagramResolver, SequenceResolver, SequenceTree, + ClassResolver, DiagramResolver, ElementResolver, LogicElement, SequenceResolver, SequenceTree, }; use puml_serializer::{ClassSerializer, ComponentSerializer}; use puml_utils::{write_fbs_to_file, write_json_to_file, LogLevel}; @@ -100,6 +100,7 @@ struct Args { enum DiagramType { None, Component, + Deployment, Class, Sequence, } @@ -226,7 +227,7 @@ fn serialize_resolved_diagram(resolved_content: &ResolvedDiagram, source_file: & #[derive(Debug, Serialize)] pub enum ResolvedDiagram { - Component(HashMap), + Component(HashMap), Class(class_diagram::ClassDiagram), Sequence(SequenceTree), } @@ -236,7 +237,7 @@ fn resolve_parsed_diagram( ) -> Result> { match parsed_content { ParsedDiagram::Component(parsed_content) => { - let mut resolver = ComponentResolver::new(); + let mut resolver = ElementResolver::new(); puml_resolver(&mut resolver, &parsed_content).map(ResolvedDiagram::Component) } ParsedDiagram::Class(parsed_content) => { @@ -293,7 +294,7 @@ fn parse_puml_file( diagram_type: DiagramType, ) -> Result> { match diagram_type { - DiagramType::Component => { + DiagramType::Component | DiagramType::Deployment => { parse_with_parser(&mut PumlComponentParser, path, content, log_level) .map(ParsedDiagram::Component) } diff --git a/plantuml/parser/puml_lobster/src/lib.rs b/plantuml/parser/puml_lobster/src/lib.rs index 3c388620..bb372fba 100644 --- a/plantuml/parser/puml_lobster/src/lib.rs +++ b/plantuml/parser/puml_lobster/src/lib.rs @@ -14,10 +14,10 @@ //! Converts the resolved PlantUML logical model into a `lobster-imp-trace` //! JSON file compatible with the LOBSTER traceability toolchain. //! -//! Only [`ComponentType::Interface`] elements are emitted +//! Only [`ElementType::Interface`] elements are emitted use class_diagram::{ClassDiagram, EntityType}; -use puml_resolver::{ComponentType, LogicComponent}; +use puml_resolver::{ElementType, LogicElement}; use serde_json::{json, Value}; use std::collections::HashMap; use std::ffi::OsStr; @@ -26,7 +26,7 @@ use std::io; use std::path::{Path, PathBuf}; pub enum LobsterModel<'a> { - Component(&'a HashMap), + Component(&'a HashMap), Class(&'a ClassDiagram), Empty, } @@ -36,11 +36,11 @@ pub enum LobsterModel<'a> { /// /// `source_path` is embedded in the `location.file` field of every emitted /// item so that LOBSTER can trace items back to their source diagram. -fn comp_model_to_lobster(model: &HashMap, source_path: &str) -> Value { +fn comp_model_to_lobster(model: &HashMap, source_path: &str) -> Value { let items: Vec = model .values() - .filter(|c| c.comp_type == ComponentType::Interface) - .map(|c| build_lobster_item(&c.id, source_path, None, "Interface")) + .filter(|element| element.element_type == ElementType::Interface) + .map(|element| build_lobster_item(&element.id, source_path, None, "Interface")) .collect(); lobster_document_from_items(items) diff --git a/plantuml/parser/puml_parser/src/component_diagram/src/component_ast.rs b/plantuml/parser/puml_parser/src/component_diagram/src/component_ast.rs index 3c508dd9..5972ac32 100644 --- a/plantuml/parser/puml_parser/src/component_diagram/src/component_ast.rs +++ b/plantuml/parser/puml_parser/src/component_diagram/src/component_ast.rs @@ -21,7 +21,7 @@ pub struct CompPumlDocument { #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] pub enum Statement { - Component(Component), + Element(Element), Relation(Relation), Port(Port), } @@ -41,8 +41,8 @@ pub enum PortType { } #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] -pub struct Component { - pub component_type: String, +pub struct Element { + pub kind: String, pub name: Option, pub alias: Option, pub stereotype: Option, diff --git a/plantuml/parser/puml_parser/src/component_diagram/src/component_parser.rs b/plantuml/parser/puml_parser/src/component_diagram/src/component_parser.rs index 1a3177fd..78f615aa 100644 --- a/plantuml/parser/puml_parser/src/component_diagram/src/component_parser.rs +++ b/plantuml/parser/puml_parser/src/component_diagram/src/component_parser.rs @@ -16,7 +16,7 @@ use std::rc::Rc; use thiserror::Error; use crate::{ - Arrow, CompPumlDocument, Component, ComponentStyle, Port, PortType, Relation, Statement, + Arrow, CompPumlDocument, ComponentStyle, Element, Port, PortType, Relation, Statement, }; use parser_core::{ format_parse_tree, pest_to_syntax_error, BaseParseError, DiagramParser, ErrorLocation, @@ -59,8 +59,8 @@ impl PumlComponentParser { ) -> Result, ComponentError> { for inner in pair.into_inner() { match inner.as_rule() { - Rule::component => { - return Ok(vec![Statement::Component(Self::parse_component(inner)?)]); + Rule::element => { + return Ok(vec![Statement::Element(Self::parse_element(inner)?)]); } Rule::relation => { return Ok(vec![Statement::Relation(Self::parse_relation(inner)?)]); @@ -115,16 +115,16 @@ impl PumlComponentParser { ) -> Result, ComponentError> { let mut stmts = Vec::new(); for inner in pair.into_inner() { - if inner.as_rule() == Rule::component_statement { + if inner.as_rule() == Rule::diagram_statement { stmts.append(&mut Self::parse_statement(inner)?); } } Ok(stmts) } - fn parse_component(pair: pest::iterators::Pair) -> Result { - let mut component = Component { - component_type: "".to_string(), + fn parse_element(pair: pest::iterators::Pair) -> Result { + let mut element = Element { + kind: "".to_string(), name: None, alias: None, stereotype: None, @@ -134,51 +134,58 @@ impl PumlComponentParser { for inner in pair.into_inner() { match inner.as_rule() { - Rule::nested_component => { - // Parse the nested component (which contains default_component or bracket_component) + Rule::nested_element => { + // Parse the nested element head (which contains default_element or bracket_element) for nested_inner in inner.into_inner() { match nested_inner.as_rule() { - Rule::default_component => { - let (ctype, name_opt) = - Self::parse_default_component(nested_inner)?; - component.component_type = ctype; - component.name = name_opt; + Rule::default_element => { + let (kind, name_opt) = Self::parse_default_element(nested_inner)?; + element.kind = kind; + element.name = name_opt; } - // For bracket_component, it's always a `component` type - Rule::bracket_component => { - let name_opt = Self::parse_bracket_component(nested_inner)?; - component.component_type = "component".to_string(); - component.name = name_opt; + // For bracket_element, it's always a `component` kind + Rule::bracket_element => { + let name_opt = Self::parse_bracket_element(nested_inner)?; + element.kind = "component".to_string(); + element.name = name_opt; } _ => {} } } } - Rule::component_old => { - component.name = Some(Self::extract_component_name(inner)); - component.component_type = "component".to_string(); + Rule::short_form_component => { + element.name = Some(Self::extract_component_name(inner)); + element.kind = "component".to_string(); } - Rule::interface_old => { - component.name = Some(Self::extract_interface_name(inner)); - component.component_type = "interface".to_string(); + Rule::short_form_actor => { + element.name = Some(Self::extract_actor_name(inner)); + element.kind = "actor".to_string(); + } + Rule::short_form_interface => { + element.name = Some(Self::extract_interface_name(inner)); + element.kind = "interface".to_string(); + } + Rule::short_form_usecase => { + element.name = Some(Self::extract_usecase_name(inner)); + element.kind = "usecase".to_string(); } Rule::alias_clause => { - component.alias = Self::extract_alias(inner); + element.alias = Self::extract_alias(inner); } Rule::stereotype => { - component.stereotype = Self::extract_stereotype(inner); + element.stereotype = Self::extract_stereotype(inner); } - Rule::component_style => { - component.style = Some(Self::parse_component_style(inner)?); + Rule::element_style => { + element.style = Some(Self::parse_component_style(inner)?); } Rule::statement_block => { - component.statements = Self::parse_statement_block(inner)?; + element.statements = Self::parse_statement_block(inner)?; } _ => {} } } - Ok(component) + Ok(element) } fn parse_relation(pair: pest::iterators::Pair) -> Result { @@ -199,7 +206,7 @@ impl PumlComponentParser { Rule::connection_arrow => { arrow = Self::parse_arrow(inner)?; } - Rule::component_description => { + Rule::relation_description => { description = Self::parse_description(inner); } _ => {} @@ -231,14 +238,28 @@ impl PumlComponentParser { // Helper methods fn extract_component_name(pair: pest::iterators::Pair) -> String { pair.into_inner() - .find(|inner| inner.as_rule() == Rule::component_old_name) + .find(|inner| inner.as_rule() == Rule::short_form_component_name) + .map(|inner| inner.as_str().to_string()) + .unwrap_or_default() + } + + fn extract_actor_name(pair: pest::iterators::Pair) -> String { + pair.into_inner() + .find(|inner| inner.as_rule() == Rule::short_form_actor_name) .map(|inner| inner.as_str().to_string()) .unwrap_or_default() } fn extract_interface_name(pair: pest::iterators::Pair) -> String { pair.into_inner() - .find(|inner| inner.as_rule() == Rule::interface_old_name) + .find(|inner| inner.as_rule() == Rule::short_form_interface_name) + .map(|inner| inner.as_str().to_string()) + .unwrap_or_default() + } + + fn extract_usecase_name(pair: pest::iterators::Pair) -> String { + pair.into_inner() + .find(|inner| inner.as_rule() == Rule::short_form_usecase_name) .map(|inner| inner.as_str().to_string()) .unwrap_or_default() } @@ -255,18 +276,18 @@ impl PumlComponentParser { .map(|inner| inner.as_str().to_string()) } - fn parse_default_component( + fn parse_default_element( pair: pest::iterators::Pair, ) -> Result<(String, Option), ComponentError> { - let mut comp_type = String::new(); + let mut kind = String::new(); let mut name = None; for inner in pair.into_inner() { match inner.as_rule() { - Rule::component_type => { - comp_type = inner.as_str().to_string(); + Rule::element_kind => { + kind = inner.as_str().to_string(); } - Rule::default_component_name => { + Rule::default_element_name => { let raw_name = inner.as_str().to_string(); // Remove surrounding quotes if present let clean_name = if raw_name.starts_with('"') && raw_name.ends_with('"') { @@ -280,16 +301,16 @@ impl PumlComponentParser { } } - Ok((comp_type, name)) + Ok((kind, name)) } - fn parse_bracket_component( + fn parse_bracket_element( pair: pest::iterators::Pair, ) -> Result, ComponentError> { let mut name: Option = None; for inner in pair.into_inner() { - if inner.as_rule() == Rule::component_old { + if inner.as_rule() == Rule::short_form_component { name = Some(Self::extract_component_name(inner)); } } @@ -314,7 +335,7 @@ impl PumlComponentParser { for inner in pair.into_inner() { match inner.as_rule() { - Rule::component_statement => { + Rule::diagram_statement => { let mut stmts = Self::parse_statement(inner)?; statements.append(&mut stmts); } @@ -340,7 +361,7 @@ impl DiagramParser for PumlComponentParser { ) -> Result { use pest::Parser; - let pairs = PlantUmlCommonParser::parse(Rule::component_start, content) + let pairs = PlantUmlCommonParser::parse(Rule::diagram_start, content) .map_err(|e| pest_to_syntax_error(e, path.as_ref().clone(), content))?; // Debug-only, excluded to keep coverage focused on parser logic. @@ -373,7 +394,7 @@ impl DiagramParser for PumlComponentParser { document.name = Some(start_inner.as_str().to_string()); } } - Rule::component_statement => { + Rule::diagram_statement => { let mut stmts = Self::parse_statement(inner_pair)?; document.statements.append(&mut stmts); } diff --git a/plantuml/parser/puml_parser/src/component_diagram/src/lib.rs b/plantuml/parser/puml_parser/src/component_diagram/src/lib.rs index 61c6023c..f006410d 100644 --- a/plantuml/parser/puml_parser/src/component_diagram/src/lib.rs +++ b/plantuml/parser/puml_parser/src/component_diagram/src/lib.rs @@ -15,6 +15,6 @@ mod component_ast; mod component_parser; pub use component_ast::{ - Arrow, CompPumlDocument, Component, ComponentStyle, Port, PortType, Relation, Statement, + Arrow, CompPumlDocument, ComponentStyle, Element, Port, PortType, Relation, Statement, }; pub use component_parser::{ComponentError, PumlComponentParser}; diff --git a/plantuml/parser/puml_parser/src/component_diagram/test/component_integration_test.rs b/plantuml/parser/puml_parser/src/component_diagram/test/component_integration_test.rs index fadc6404..0517d0bd 100644 --- a/plantuml/parser/puml_parser/src/component_diagram/test/component_integration_test.rs +++ b/plantuml/parser/puml_parser/src/component_diagram/test/component_integration_test.rs @@ -267,9 +267,9 @@ fn test_component_golden() { r#"CompPumlDocument { name: None, statements: [ - Component( - Component { - component_type: "component", + Element( + Element { + kind: "component", name: Some( "First component", ), @@ -279,9 +279,9 @@ fn test_component_golden() { statements: [], }, ), - Component( - Component { - component_type: "component", + Element( + Element { + kind: "component", name: Some( "Another component", ), @@ -293,9 +293,9 @@ fn test_component_golden() { statements: [], }, ), - Component( - Component { - component_type: "component", + Element( + Element { + kind: "component", name: Some( "Comp3", ), @@ -305,9 +305,9 @@ fn test_component_golden() { statements: [], }, ), - Component( - Component { - component_type: "component", + Element( + Element { + kind: "component", name: Some( "Last\\ncomponent", ), @@ -328,9 +328,9 @@ fn test_component_golden() { r#"CompPumlDocument { name: None, statements: [ - Component( - Component { - component_type: "component", + Element( + Element { + kind: "component", name: Some( "First component", ), @@ -340,9 +340,9 @@ fn test_component_golden() { statements: [], }, ), - Component( - Component { - component_type: "component", + Element( + Element { + kind: "component", name: Some( "Another component", ), @@ -354,9 +354,9 @@ fn test_component_golden() { statements: [], }, ), - Component( - Component { - component_type: "component", + Element( + Element { + kind: "component", name: Some( "Comp3", ), @@ -366,9 +366,9 @@ fn test_component_golden() { statements: [], }, ), - Component( - Component { - component_type: "component", + Element( + Element { + kind: "component", name: Some( "Last\\ncomponent", ), @@ -389,9 +389,9 @@ fn test_component_golden() { r#"CompPumlDocument { name: None, statements: [ - Component( - Component { - component_type: "component", + Element( + Element { + kind: "component", name: Some( "C1", ), @@ -401,9 +401,9 @@ fn test_component_golden() { statements: [], }, ), - Component( - Component { - component_type: "component", + Element( + Element { + kind: "component", name: Some( "C2", ), @@ -413,9 +413,9 @@ fn test_component_golden() { statements: [], }, ), - Component( - Component { - component_type: "component", + Element( + Element { + kind: "component", name: Some( "C3", ), @@ -450,9 +450,9 @@ fn test_component_golden() { r#"CompPumlDocument { name: None, statements: [ - Component( - Component { - component_type: "component", + Element( + Element { + kind: "component", name: Some( "Web Server", ), @@ -476,9 +476,9 @@ fn test_component_golden() { r#"CompPumlDocument { name: None, statements: [ - Component( - Component { - component_type: "interface", + Element( + Element { + kind: "interface", name: Some( "\"First Interface\"", ), @@ -488,9 +488,9 @@ fn test_component_golden() { statements: [], }, ), - Component( - Component { - component_type: "interface", + Element( + Element { + kind: "interface", name: Some( "\"Another interface\"", ), @@ -502,9 +502,9 @@ fn test_component_golden() { statements: [], }, ), - Component( - Component { - component_type: "interface", + Element( + Element { + kind: "interface", name: Some( "Interf3", ), @@ -514,9 +514,9 @@ fn test_component_golden() { statements: [], }, ), - Component( - Component { - component_type: "interface", + Element( + Element { + kind: "interface", name: Some( "Last\\ninterface", ), @@ -528,9 +528,9 @@ fn test_component_golden() { statements: [], }, ), - Component( - Component { - component_type: "component", + Element( + Element { + kind: "component", name: Some( "component", ), @@ -549,9 +549,9 @@ fn test_component_golden() { r#"CompPumlDocument { name: None, statements: [ - Component( - Component { - component_type: "component", + Element( + Element { + kind: "component", name: Some( "comp1", ), @@ -570,9 +570,9 @@ fn test_component_golden() { r#"CompPumlDocument { name: None, statements: [ - Component( - Component { - component_type: "interface", + Element( + Element { + kind: "interface", name: Some( "Data Access", ), diff --git a/plantuml/parser/puml_parser/src/grammar/common.pest b/plantuml/parser/puml_parser/src/grammar/common.pest index 93504c92..7ca0de08 100644 --- a/plantuml/parser/puml_parser/src/grammar/common.pest +++ b/plantuml/parser/puml_parser/src/grammar/common.pest @@ -271,7 +271,7 @@ arrow_middle = { | arrow_mid_decor } -arrow_mid_decor = @{ "0" | "(0" | "0)" | "(0)" | "()" | "(" | ")" } +arrow_mid_decor = @{ "(0)" | "(0" | "0)" | "()" | "(" | ")" | "0" } // ---------- Direction ---------- arrow_direction = @{ diff --git a/plantuml/parser/puml_parser/src/grammar/component.pest b/plantuml/parser/puml_parser/src/grammar/component.pest index 40abb705..beb01638 100644 --- a/plantuml/parser/puml_parser/src/grammar/component.pest +++ b/plantuml/parser/puml_parser/src/grammar/component.pest @@ -13,58 +13,67 @@ // PlantUML Component Diagram Grammar for Pest Parser -component_start = { empty_line* ~ startuml ~ (component_statement | empty_line)* ~ enduml } +diagram_start = { empty_line* ~ startuml ~ (diagram_statement | empty_line)* ~ enduml } -component_statement = { ((relation | together_block | port_declaration | component | footer_line) ~ EOL) } +diagram_statement = { ((relation | together_block | port_declaration | element | footer_line) ~ EOL) } port_declaration = { port_keyword ~ port_name ~ alias_clause? } port_keyword = @{ "portin" | "portout" | "port" } port_name = { CNAME } -together_block = { "together" ~ "{" ~ EOL ~ (component_statement | empty_line)* ~ "}" } +together_block = { "together" ~ "{" ~ EOL ~ (diagram_statement | empty_line)* ~ "}" } -component = { - (nested_component ~ alias_clause? ~ stereotype? ~ component_style? ~ (long_description | statement_block)? ) | - ((interface_old | component_old) ~ alias_clause? ~ component_style?) +element = { + (nested_element ~ alias_clause? ~ stereotype? ~ element_style? ~ (long_description | statement_block)? ) | + ((short_form_actor | short_form_interface | short_form_component | short_form_usecase) ~ alias_clause? ~ element_style?) } long_description = {"[" ~ EOL? ~ (ASCII_ALPHANUMERIC ~ EOL?)* ~ "]"} -component_old = { "[" ~ component_old_name ~ "]" } -interface_old = { "()" ~ interface_old_name } +short_form_actor = { ":" ~ short_form_actor_name ~ ":" } +short_form_component = { "[" ~ short_form_component_name ~ "]" } +short_form_interface = { "()" ~ short_form_interface_name } +short_form_usecase = { "(" ~ short_form_usecase_name ~ ")" } -component_old_name = { BRACKET_NAME | CNAME } -interface_old_name = { CNAME } +short_form_actor_name = { BRACKET_NAME | CNAME } +short_form_component_name = { BRACKET_NAME | CNAME } +short_form_interface_name = { CNAME } +short_form_usecase_name = { BRACKET_NAME | CNAME } -default_component = { component_type ~ default_component_name? } -bracket_component = { "component" ~ component_old } +default_element = { element_kind ~ default_element_name? } +bracket_element = { "component" ~ short_form_component } -default_component_name = { CNAME } +default_element_name = { CNAME } -nested_component = { bracket_component | default_component } +nested_element = { bracket_element | default_element } -statement_block = { "{" ~ EOL ~ (component_statement | empty_line)* ~ "}" } +statement_block = { "{" ~ EOL ~ (diagram_statement | empty_line)* ~ "}" } -relation = { relation_left ~ connection_arrow ~ relation_right ~ component_style? ~ component_description?} -relation_left = { QUALIFIED_NAME | component_old | interface_old } -relation_right = { QUALIFIED_NAME | component_old | interface_old } +relation = { relation_left ~ connection_arrow ~ relation_right ~ element_style? ~ relation_description?} +relation_left = { QUALIFIED_NAME | short_form_actor | short_form_component | short_form_interface | short_form_usecase } +relation_right = { QUALIFIED_NAME | short_form_actor | short_form_component | short_form_interface | short_form_usecase } // Terminals QUALIFIED_NAME = @{ NAME ~ ("." ~ NAME)* } BRACKET_NAME = @{ (ASCII_ALPHANUMERIC | "_" | "-" | " " | "\\")+ } -component_type = { COMPONENT_TYPE } -COMPONENT_TYPE = { +element_kind = { component_kind | deployment_kind } + +component_kind = { "artifact" | "card" | "cloud" | "component" | "database" | "file" | "folder" | "frame" | "hexagon" | "interface" | "node" | "package" | "queue" | "rectangle" | "stack" | "storage" } -footer_line = { footer_align? ~ ^"footer" ~ ":"? ~ component_text_content? ~ COMMENT* } +deployment_kind = { + "actor" | "agent" | "boundary" | "control" | "entity" | "usecase" +} + +footer_line = { footer_align? ~ ^"footer" ~ ":"? ~ diagram_text_content? ~ COMMENT* } footer_block_start = { footer_align? ~ ^"footer" } footer_block_end = { ^"end" ~ WHITESPACE? ~ ^"footer" } footer_align = { ^"left" | ^"right" | ^"center" } -component_text_content = { (!EOL ~ !comment_start ~ ANY)+ } +diagram_text_content = { (!EOL ~ !comment_start ~ ANY)+ } comment_start = _{ "//" | "'" | "/'" } // This is **bold** @@ -74,7 +83,7 @@ comment_start = _{ "//" | "'" | "/'" } // This is __underlined__ // This is ~~wave-underlined~~ -component_style = { +element_style = { ("#" ~ component_color ~ (";" ~ component_attr)* ~ ";") | ("#" ~ component_color ~ (";" ~ component_attr)*) | ("#" ~ component_attr ~ (";" ~ component_attr)* ~ ";") | @@ -92,5 +101,5 @@ COMPONENT_LINE_ATTR = @{ "dashed" | "bold" | "dotted" } COMPONENT_TEXT_COLOR = @{ "text" } COMPONENT_LINE_COLOR = @{ "line" } -component_description = { ":" ~ description_text } +relation_description = { ":" ~ description_text } description_text = @{ (!("\n" | "\r") ~ ANY)* } diff --git a/plantuml/parser/puml_parser/src/lib.rs b/plantuml/parser/puml_parser/src/lib.rs index 11334f08..cee8f4cb 100644 --- a/plantuml/parser/puml_parser/src/lib.rs +++ b/plantuml/parser/puml_parser/src/lib.rs @@ -13,7 +13,7 @@ // Re-export commonly used items that don't have name conflicts pub use class_parser::{ClassError, ClassUmlFile, PumlClassParser}; -pub use component_parser::{CompPumlDocument, ComponentError, PumlComponentParser}; +pub use component_parser::{CompPumlDocument, ComponentError, Element, PumlComponentParser}; pub use parser_core::{ common_ast, common_parser, Arrow, BaseParseError, DiagramParser, ErrorLocation, }; diff --git a/plantuml/parser/puml_resolver/src/component_diagram/BUILD b/plantuml/parser/puml_resolver/src/component_diagram/BUILD index 06a1ada6..64d4a402 100644 --- a/plantuml/parser/puml_resolver/src/component_diagram/BUILD +++ b/plantuml/parser/puml_resolver/src/component_diagram/BUILD @@ -39,6 +39,7 @@ rust_test( crate_root = "tests/component_resolver_test.rs", data = [ "//plantuml/parser/integration_test/component_diagram:component_diagram_files", + "//plantuml/parser/integration_test/deployment_diagram:deployment_diagram_files", ], visibility = ["//plantuml/parser:__subpackages__"], deps = [ diff --git a/plantuml/parser/puml_resolver/src/component_diagram/src/component_logic.rs b/plantuml/parser/puml_resolver/src/component_diagram/src/component_logic.rs index 2a6e15c9..ff0789c2 100644 --- a/plantuml/parser/puml_resolver/src/component_diagram/src/component_logic.rs +++ b/plantuml/parser/puml_resolver/src/component_diagram/src/component_logic.rs @@ -14,28 +14,34 @@ use serde::{Deserialize, Serialize}; // #[derive(Debug, Clone)] // pub struct Package { -// pub components: Vec, +// pub elements: Vec, // } #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] -pub struct LogicComponent { +pub struct LogicElement { pub id: String, //FQN pub name: Option, pub alias: Option, - pub parent_id: Option, // FQN of parent - pub comp_type: ComponentType, // e.g., package, component, etc. + pub parent_id: Option, // FQN of parent + #[serde(rename = "element_type", alias = "comp_type")] + pub element_type: ElementType, // e.g., package, component, etc. pub stereotype: Option, // e.g., component, unit, etc. pub relations: Vec, } #[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)] #[serde(rename_all = "PascalCase")] -pub enum ComponentType { +pub enum ElementType { Artifact, + Actor, + Agent, + Boundary, Card, Cloud, Component, + Control, Database, + Entity, File, Folder, Frame, @@ -47,6 +53,7 @@ pub enum ComponentType { Rectangle, Stack, Storage, + Usecase, } #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] @@ -57,13 +64,17 @@ pub struct LogicRelation { } #[derive(Debug, thiserror::Error)] -pub enum ComponentResolverError { - #[error("Component Resolver: UnresolvedReference: {reference}")] +pub enum ElementResolverError { + #[error("Element Resolver: UnresolvedReference: {reference}")] UnresolvedReference { reference: String }, - #[error("Duplicate component id: {component_id}")] - DuplicateComponent { component_id: String }, + #[error("Duplicate element id: {element_id}")] + DuplicateElement { element_id: String }, - #[error("Unknown component type: {component_type}")] - UnknownComponentType { component_type: String }, + #[error("Unknown element type: {element_type}")] + UnknownElementType { element_type: String }, } + +pub type LogicComponent = LogicElement; +pub type ComponentType = ElementType; +pub type ComponentResolverError = ElementResolverError; diff --git a/plantuml/parser/puml_resolver/src/component_diagram/src/component_resolver.rs b/plantuml/parser/puml_resolver/src/component_diagram/src/component_resolver.rs index c112bbb4..84c55ea4 100644 --- a/plantuml/parser/puml_resolver/src/component_diagram/src/component_resolver.rs +++ b/plantuml/parser/puml_resolver/src/component_diagram/src/component_resolver.rs @@ -14,25 +14,23 @@ use log::error; use std::collections::HashMap; -use crate::component_logic::{ - ComponentResolverError, ComponentType, LogicComponent, LogicRelation, -}; -use component_parser::{CompPumlDocument, Component, Port, Statement}; +use crate::component_logic::{ElementResolverError, ElementType, LogicElement, LogicRelation}; +use component_parser::{CompPumlDocument, Element, Port, Statement}; use resolver_traits::DiagramResolver; #[derive(Default)] -pub struct ComponentResolver { - pub scope: Vec, // component id stack - pub components: HashMap, // FQN -> LogicComponent - /// Maps port FQN → parent component FQN (for relation lifting) +pub struct ElementResolver { + pub scope: Vec, // element id stack + pub elements: HashMap, // FQN -> LogicElement + /// Maps port FQN → parent element FQN (for relation lifting) pub port_parents: HashMap, } -impl ComponentResolver { +impl ElementResolver { pub fn new() -> Self { Self { scope: Vec::new(), - components: HashMap::new(), + elements: HashMap::new(), port_parents: HashMap::new(), } } @@ -49,29 +47,33 @@ impl ComponentResolver { /// 1) Simple name: search upward from current scope + recurse into children /// 2) Relative qualified name: path starting from current scope /// 3) Absolute FQN: full path - fn resolve_ref(&self, raw: &str) -> Result { + fn resolve_ref(&self, raw: &str) -> Result { let parts: Vec<&str> = raw.split('.').collect(); - // Helper: recursively search for a component FQN within the given scope and its children + // Helper: recursively search for an element FQN within the given scope and its children fn find_in_scope_or_children( scope: &[String], parts: &[&str], - components: &HashMap, + elements: &HashMap, ) -> Option { let mut candidate = scope.to_vec(); candidate.extend(parts.iter().map(|s| s.to_string())); let fqn = candidate.join("."); - if components.contains_key(&fqn) { + if elements.contains_key(&fqn) { return Some(fqn); } - for comp in components.values() { - if let Some(parent) = &comp.parent_id { + for element in elements.values() { + if let Some(parent) = &element.parent_id { if parent == &scope.join(".") { let mut child_scope = scope.to_vec(); - child_scope.push(comp.alias.clone().unwrap_or(comp.name.clone().unwrap())); - if let Some(f) = find_in_scope_or_children(&child_scope, parts, components) - { + child_scope.push( + element + .alias + .clone() + .unwrap_or(element.name.clone().unwrap()), + ); + if let Some(f) = find_in_scope_or_children(&child_scope, parts, elements) { return Some(f); } } @@ -82,7 +84,7 @@ impl ComponentResolver { } // Helper: search for a port by local name within the given scope and any of its - // descendants, returning the port's parent component FQN when found. + // descendants, returning the port's parent element FQN when found. fn find_port_in_scope_or_children( scope: &[String], port_local: &str, @@ -97,7 +99,7 @@ impl ComponentResolver { } // Search at any depth below the current scope: a port whose simple alias matches - // and whose parent component is a descendant of (or equal to) the current scope. + // and whose parent element is a descendant of (or equal to) the current scope. let scope_prefix = scope.join("."); for (pfqn, parent_comp) in port_parents { let parts: Vec<&str> = pfqn.split('.').collect(); @@ -119,18 +121,18 @@ impl ComponentResolver { if parts.len() == 1 { for i in (0..=self.scope.len()).rev() { let outer_scope = &self.scope[..i]; - if let Some(fqn) = find_in_scope_or_children(outer_scope, &parts, &self.components) - { + if let Some(fqn) = find_in_scope_or_children(outer_scope, &parts, &self.elements) { return Ok(fqn); } } - for comp in self.components.values() { - if comp.alias.as_deref() == Some(parts[0]) || comp.name.as_deref() == Some(parts[0]) + for element in self.elements.values() { + if element.alias.as_deref() == Some(parts[0]) + || element.name.as_deref() == Some(parts[0]) { - return Ok(comp.id.clone()); + return Ok(element.id.clone()); } } - // Fallback: check if it's a port name and lift to parent component. + // Fallback: check if it's a port name and lift to the parent element. // Search upward through scope levels — the innermost scope that contains a // port with this alias wins (nearest-scope-first). for i in (0..=self.scope.len()).rev() { @@ -144,27 +146,27 @@ impl ComponentResolver { } // 2) Relative qualified name + recurse into children - if let Some(fqn) = find_in_scope_or_children(&self.scope, &parts, &self.components) { + if let Some(fqn) = find_in_scope_or_children(&self.scope, &parts, &self.elements) { return Ok(fqn); } // 3) Absolute FQN let fqn = parts.join("."); - if self.components.contains_key(&fqn) { + if self.elements.contains_key(&fqn) { return Ok(fqn); } error!("Unresolved reference: {}", raw); - Err(ComponentResolverError::UnresolvedReference { + Err(ElementResolverError::UnresolvedReference { reference: raw.to_string(), }) } } -impl DiagramResolver for ComponentResolver { +impl DiagramResolver for ElementResolver { type Document = CompPumlDocument; - type Output = HashMap; - type Error = ComponentResolverError; + type Output = HashMap; + type Error = ElementResolverError; fn resolve(&mut self, document: &CompPumlDocument) -> Result { self.scope.clear(); @@ -173,18 +175,18 @@ impl DiagramResolver for ComponentResolver { self.visit_statement(stmt)?; } - // Post-pass: lift port references to parent component + // Post-pass: lift port references to their parent element self.lift_port_relations(); - Ok(self.components.clone()) + Ok(self.elements.clone()) } } -impl ComponentResolver { - fn visit_statement(&mut self, statement: &Statement) -> Result<(), ComponentResolverError> { +impl ElementResolver { + fn visit_statement(&mut self, statement: &Statement) -> Result<(), ElementResolverError> { match statement { - Statement::Component(component) => { - self.visit_component(component)?; + Statement::Element(element) => { + self.visit_element(element)?; Ok(()) } Statement::Port(port) => { @@ -195,22 +197,22 @@ impl ComponentResolver { let src_fqn = self.resolve_ref(&relation.lhs)?; let tgt_fqn = self.resolve_ref(&relation.rhs)?; - if let Some(source_component) = self.components.get_mut(&src_fqn) { - source_component.relations.push(LogicRelation { + if let Some(source_element) = self.elements.get_mut(&src_fqn) { + source_element.relations.push(LogicRelation { target: tgt_fqn, annotation: relation.description.clone(), relation_type: "None".to_string(), // Placeholder, can be enhanced to capture relation type from arrow }); Ok(()) } else { - Err(ComponentResolverError::UnresolvedReference { reference: src_fqn }) + Err(ElementResolverError::UnresolvedReference { reference: src_fqn }) } } } } } -impl ComponentResolver { +impl ElementResolver { fn visit_port(&mut self, port: &Port) { let local_id = port.alias.as_deref().unwrap_or(&port.name); let fqn = self.make_fqn(local_id); @@ -225,12 +227,12 @@ impl ComponentResolver { } /// After all statements are visited, replace any relation endpoint that is a - /// port FQN with the port's parent component FQN. + /// port FQN with the port's parent element FQN. fn lift_port_relations(&mut self) { let port_parents = self.port_parents.clone(); - for comp in self.components.values_mut() { - for rel in comp.relations.iter_mut() { + for element in self.elements.values_mut() { + for rel in element.relations.iter_mut() { if let Some(parent) = port_parents.get(&rel.target) { rel.target = parent.clone(); } @@ -238,16 +240,16 @@ impl ComponentResolver { } } - fn visit_component(&mut self, component: &Component) -> Result<(), ComponentResolverError> { - let local_id = component + fn visit_element(&mut self, element: &Element) -> Result<(), ElementResolverError> { + let local_id = element .alias .as_deref() - .or(component.name.as_deref()) - .expect("Component must have name or alias (guaranteed by grammar)"); + .or(element.name.as_deref()) + .expect("Element must have name or alias (guaranteed by grammar)"); let fqn = self.make_fqn(local_id); - if self.components.contains_key(&fqn) { - return Err(ComponentResolverError::DuplicateComponent { component_id: fqn }); + if self.elements.contains_key(&fqn) { + return Err(ElementResolverError::DuplicateElement { element_id: fqn }); } let parent_id = if self.scope.is_empty() { @@ -256,21 +258,21 @@ impl ComponentResolver { Some(self.scope.join(".")) }; - let logic = LogicComponent { + let logic = LogicElement { id: fqn.clone(), - name: component.name.clone(), - alias: component.alias.clone(), + name: element.name.clone(), + alias: element.alias.clone(), parent_id, - comp_type: parse_component_type(&component.component_type)?, - stereotype: component.stereotype.clone(), + element_type: parse_kind(&element.kind)?, + stereotype: element.stereotype.clone(), relations: Vec::new(), }; - self.components.insert(fqn.clone(), logic); + self.elements.insert(fqn.clone(), logic); self.scope.push(local_id.to_string()); - for stmt in &component.statements { + for stmt in &element.statements { self.visit_statement(stmt)?; } @@ -280,31 +282,39 @@ impl ComponentResolver { } } -const COMPONENT_TYPE_TABLE: &[(&str, ComponentType)] = &[ - ("artifact", ComponentType::Artifact), - ("card", ComponentType::Card), - ("cloud", ComponentType::Cloud), - ("component", ComponentType::Component), - ("database", ComponentType::Database), - ("file", ComponentType::File), - ("folder", ComponentType::Folder), - ("frame", ComponentType::Frame), - ("hexagon", ComponentType::Hexagon), - ("interface", ComponentType::Interface), - ("node", ComponentType::Node), - ("package", ComponentType::Package), - ("queue", ComponentType::Queue), - ("rectangle", ComponentType::Rectangle), - ("stack", ComponentType::Stack), - ("storage", ComponentType::Storage), +const ELEMENT_TYPE_TABLE: &[(&str, ElementType)] = &[ + ("artifact", ElementType::Artifact), + ("actor", ElementType::Actor), + ("agent", ElementType::Agent), + ("boundary", ElementType::Boundary), + ("card", ElementType::Card), + ("cloud", ElementType::Cloud), + ("component", ElementType::Component), + ("control", ElementType::Control), + ("database", ElementType::Database), + ("entity", ElementType::Entity), + ("file", ElementType::File), + ("folder", ElementType::Folder), + ("frame", ElementType::Frame), + ("hexagon", ElementType::Hexagon), + ("interface", ElementType::Interface), + ("node", ElementType::Node), + ("package", ElementType::Package), + ("queue", ElementType::Queue), + ("rectangle", ElementType::Rectangle), + ("stack", ElementType::Stack), + ("storage", ElementType::Storage), + ("usecase", ElementType::Usecase), ]; -pub fn parse_component_type(raw: &str) -> Result { - COMPONENT_TYPE_TABLE +pub fn parse_kind(raw: &str) -> Result { + ELEMENT_TYPE_TABLE .iter() .find(|(k, _)| k.eq_ignore_ascii_case(raw)) .map(|(_, v)| *v) - .ok_or_else(|| ComponentResolverError::UnknownComponentType { - component_type: raw.into(), + .ok_or_else(|| ElementResolverError::UnknownElementType { + element_type: raw.into(), }) } + +pub type ComponentResolver = ElementResolver; diff --git a/plantuml/parser/puml_resolver/src/component_diagram/src/lib.rs b/plantuml/parser/puml_resolver/src/component_diagram/src/lib.rs index b95ed52a..1dc817a6 100644 --- a/plantuml/parser/puml_resolver/src/component_diagram/src/lib.rs +++ b/plantuml/parser/puml_resolver/src/component_diagram/src/lib.rs @@ -14,5 +14,8 @@ mod component_logic; mod component_resolver; -pub use component_logic::{ComponentResolverError, ComponentType, LogicComponent}; -pub use component_resolver::ComponentResolver; +pub use component_logic::{ + ComponentResolverError, ComponentType, ElementResolverError, ElementType, LogicComponent, + LogicElement, +}; +pub use component_resolver::{ComponentResolver, ElementResolver}; diff --git a/plantuml/parser/puml_resolver/src/component_diagram/tests/component_resolver_test.rs b/plantuml/parser/puml_resolver/src/component_diagram/tests/component_resolver_test.rs index 169a71ce..4d479a5f 100644 --- a/plantuml/parser/puml_resolver/src/component_diagram/tests/component_resolver_test.rs +++ b/plantuml/parser/puml_resolver/src/component_diagram/tests/component_resolver_test.rs @@ -16,7 +16,7 @@ use std::path::PathBuf; use std::rc::Rc; use component_parser::PumlComponentParser; -use component_resolver::{ComponentResolver, ComponentResolverError, LogicComponent}; +use component_resolver::{ElementResolver, ElementResolverError, LogicElement}; use parser_core::DiagramParser; use puml_utils::LogLevel; use resolver_traits::DiagramResolver; @@ -25,16 +25,16 @@ use test_framework::{run_case, DefaultExpectationChecker, DiagramProcessor}; // ===== Component Resolver adapter DiagramProcessor ===== struct ComponentResolverRunner; impl DiagramProcessor for ComponentResolverRunner { - type Output = HashMap; - type Error = ComponentResolverError; + type Output = HashMap; + type Error = ElementResolverError; fn run( &self, files: &HashSet>, - ) -> Result, HashMap>, ComponentResolverError> { + ) -> Result, HashMap>, ElementResolverError> { let mut results = HashMap::new(); let mut parser = PumlComponentParser; - let mut resolver = ComponentResolver::new(); + let mut resolver = ElementResolver::new(); for path in files { let puml_file = fs::read_to_string(&**path).expect("Failed to read test file"); @@ -59,6 +59,15 @@ fn run_component_resolver_case(case_name: &str) { ); } +fn run_deployment_resolver_case(case_name: &str) { + run_case( + "integration_test/deployment_diagram", + case_name, + ComponentResolverRunner, + DefaultExpectationChecker, + ); +} + #[test] fn test_relation_simple_name() { run_component_resolver_case("relation_simple_name"); @@ -143,3 +152,23 @@ fn test_top_level_port() { fn test_port_deep_nesting() { run_component_resolver_case("port_deep_nesting"); } + +#[test] +fn test_deployment_diagram() { + run_deployment_resolver_case("deployment_diagram_it"); +} + +#[test] +fn test_declare_elements() { + run_deployment_resolver_case("declare_elements"); +} + +#[test] +fn test_arrows_link() { + run_deployment_resolver_case("arrows_link"); +} + +#[test] +fn test_nested_elements() { + run_deployment_resolver_case("nested_elements"); +} diff --git a/plantuml/parser/puml_serializer/src/fbs/component.fbs b/plantuml/parser/puml_serializer/src/fbs/component.fbs index c81c9706..ca9c2660 100644 --- a/plantuml/parser/puml_serializer/src/fbs/component.fbs +++ b/plantuml/parser/puml_serializer/src/fbs/component.fbs @@ -21,10 +21,15 @@ table LogicRelation { enum ComponentType:byte { Artifact, + Actor, + Agent, + Boundary, Card, Cloud, Component, + Control, Database, + Entity, File, Folder, Frame, @@ -35,7 +40,8 @@ enum ComponentType:byte { Queue, Rectangle, Stack, - Storage + Storage, + Usecase } table LogicComponent { diff --git a/plantuml/parser/puml_serializer/src/serialize/component_serializer.rs b/plantuml/parser/puml_serializer/src/serialize/component_serializer.rs index 78740fe6..d886ed69 100644 --- a/plantuml/parser/puml_serializer/src/serialize/component_serializer.rs +++ b/plantuml/parser/puml_serializer/src/serialize/component_serializer.rs @@ -15,23 +15,23 @@ use flatbuffers::FlatBufferBuilder; use std::collections::HashMap; use component_fbs::component as fb; -use component_resolver::{ComponentType, LogicComponent}; +use component_resolver::{ElementType, LogicElement}; pub struct ComponentSerializer; impl ComponentSerializer { - pub fn serialize(components: &HashMap, source_file: &str) -> Vec { + pub fn serialize(elements: &HashMap, source_file: &str) -> Vec { let mut builder = FlatBufferBuilder::new(); // -------------------------- // 1) build components // -------------------------- - let mut comps_map = Vec::with_capacity(components.len()); + let mut comps_map = Vec::with_capacity(elements.len()); - for comp in components.values() { + for element in elements.values() { let mut relation_offsets = Vec::new(); - for r in &comp.relations { + for r in &element.relations { let target_offset = builder.create_string(&r.target); let annotation_offset = r.annotation.as_ref().map(|s| builder.create_string(s)); let relation_type_offset = builder.create_string(&r.relation_type); @@ -50,11 +50,15 @@ impl ComponentSerializer { let relations_vector_offset = builder.create_vector(&relation_offsets); // component - let comp_id_offset = builder.create_string(&comp.id); - let comp_name_offset = comp.name.as_ref().map(|s| builder.create_string(s)); - let comp_alias_offset = comp.alias.as_ref().map(|s| builder.create_string(s)); - let comp_parent_id_offset = comp.parent_id.as_ref().map(|s| builder.create_string(s)); - let comp_stereotype_offset = comp.stereotype.as_ref().map(|s| builder.create_string(s)); + let comp_id_offset = builder.create_string(&element.id); + let comp_name_offset = element.name.as_ref().map(|s| builder.create_string(s)); + let comp_alias_offset = element.alias.as_ref().map(|s| builder.create_string(s)); + let comp_parent_id_offset = + element.parent_id.as_ref().map(|s| builder.create_string(s)); + let comp_stereotype_offset = element + .stereotype + .as_ref() + .map(|s| builder.create_string(s)); let comp_offset = fb::LogicComponent::create( &mut builder, @@ -63,13 +67,13 @@ impl ComponentSerializer { name: comp_name_offset, alias: comp_alias_offset, parent_id: comp_parent_id_offset, - comp_type: Self::convert_type(comp.comp_type), + comp_type: Self::convert_type(element.element_type), stereotype: comp_stereotype_offset, relations: Some(relations_vector_offset), }, ); - let key_offset = builder.create_string(&comp.id); + let key_offset = builder.create_string(&element.id); let comp_map = fb::ComponentMap::create( &mut builder, &fb::ComponentMapArgs { @@ -106,24 +110,30 @@ impl ComponentSerializer { builder.finished_data().to_vec() } - fn convert_type(t: ComponentType) -> fb::ComponentType { + fn convert_type(t: ElementType) -> fb::ComponentType { match t { - ComponentType::Artifact => fb::ComponentType::Artifact, - ComponentType::Card => fb::ComponentType::Card, - ComponentType::Cloud => fb::ComponentType::Cloud, - ComponentType::Component => fb::ComponentType::Component, - ComponentType::Database => fb::ComponentType::Database, - ComponentType::File => fb::ComponentType::File, - ComponentType::Folder => fb::ComponentType::Folder, - ComponentType::Frame => fb::ComponentType::Frame, - ComponentType::Hexagon => fb::ComponentType::Hexagon, - ComponentType::Interface => fb::ComponentType::Interface, - ComponentType::Node => fb::ComponentType::Node, - ComponentType::Package => fb::ComponentType::Package, - ComponentType::Queue => fb::ComponentType::Queue, - ComponentType::Rectangle => fb::ComponentType::Rectangle, - ComponentType::Stack => fb::ComponentType::Stack, - ComponentType::Storage => fb::ComponentType::Storage, + ElementType::Artifact => fb::ComponentType::Artifact, + ElementType::Actor => fb::ComponentType::Actor, + ElementType::Agent => fb::ComponentType::Agent, + ElementType::Boundary => fb::ComponentType::Boundary, + ElementType::Card => fb::ComponentType::Card, + ElementType::Cloud => fb::ComponentType::Cloud, + ElementType::Component => fb::ComponentType::Component, + ElementType::Control => fb::ComponentType::Control, + ElementType::Database => fb::ComponentType::Database, + ElementType::Entity => fb::ComponentType::Entity, + ElementType::File => fb::ComponentType::File, + ElementType::Folder => fb::ComponentType::Folder, + ElementType::Frame => fb::ComponentType::Frame, + ElementType::Hexagon => fb::ComponentType::Hexagon, + ElementType::Interface => fb::ComponentType::Interface, + ElementType::Node => fb::ComponentType::Node, + ElementType::Package => fb::ComponentType::Package, + ElementType::Queue => fb::ComponentType::Queue, + ElementType::Rectangle => fb::ComponentType::Rectangle, + ElementType::Stack => fb::ComponentType::Stack, + ElementType::Storage => fb::ComponentType::Storage, + ElementType::Usecase => fb::ComponentType::Usecase, } } }