diff --git a/plantuml/parser/integration_test/component_diagram/plantuml/component/output.json b/plantuml/parser/integration_test/component_diagram/plantuml/component/output.json index 6edc0d1f..1b81e7c8 100644 --- a/plantuml/parser/integration_test/component_diagram/plantuml/component/output.json +++ b/plantuml/parser/integration_test/component_diagram/plantuml/component/output.json @@ -4,58 +4,66 @@ "statements": [ { "Element": { - "kind": "component", - "name": "Telemetry collector", - "alias": null, - "stereotype": null, + "identity": { + "name": "Telemetry collector", + "alias": null, + "stereotype": null, + "element_kind": "component", + "source_location": { + "file": "", + "line": 15 + } + }, "style": null, - "statements": [], - "source_location": { - "file": "", - "line": 15 - } + "statements": [] } }, { "Element": { - "kind": "component", - "name": "Calibration service", - "alias": "CalibrationSvc", - "stereotype": null, + "identity": { + "name": "Calibration service", + "alias": "CalibrationSvc", + "stereotype": null, + "element_kind": "component", + "source_location": { + "file": "", + "line": 16 + } + }, "style": null, - "statements": [], - "source_location": { - "file": "", - "line": 16 - } + "statements": [] } }, { "Element": { - "kind": "component", - "name": "HealthMonitor", - "alias": null, - "stereotype": null, + "identity": { + "name": "HealthMonitor", + "alias": null, + "stereotype": null, + "element_kind": "component", + "source_location": { + "file": "", + "line": 17 + } + }, "style": null, - "statements": [], - "source_location": { - "file": "", - "line": 17 - } + "statements": [] } }, { "Element": { - "kind": "component", - "name": "Fallback\\nrecorder", - "alias": "FallbackRecorder", - "stereotype": null, + "identity": { + "name": "Fallback\\nrecorder", + "alias": "FallbackRecorder", + "stereotype": null, + "element_kind": "component", + "source_location": { + "file": "", + "line": 18 + } + }, "style": null, - "statements": [], - "source_location": { - "file": "", - "line": 18 - } + "statements": [] } } ] diff --git a/plantuml/parser/integration_test/component_diagram/plantuml/grouping_components/output.json b/plantuml/parser/integration_test/component_diagram/plantuml/grouping_components/output.json index 887c7d6b..ac8ce730 100644 --- a/plantuml/parser/integration_test/component_diagram/plantuml/grouping_components/output.json +++ b/plantuml/parser/integration_test/component_diagram/plantuml/grouping_components/output.json @@ -4,58 +4,66 @@ "statements": [ { "Element": { - "kind": "component", - "name": "First component", - "alias": null, - "stereotype": null, + "identity": { + "name": "First component", + "alias": null, + "stereotype": null, + "element_kind": "component", + "source_location": { + "file": "", + "line": 15 + } + }, "style": null, - "statements": [], - "source_location": { - "file": "", - "line": 15 - } + "statements": [] } }, { "Element": { - "kind": "component", - "name": "Another component", - "alias": "Comp2", - "stereotype": null, + "identity": { + "name": "Another component", + "alias": "Comp2", + "stereotype": null, + "element_kind": "component", + "source_location": { + "file": "", + "line": 16 + } + }, "style": null, - "statements": [], - "source_location": { - "file": "", - "line": 16 - } + "statements": [] } }, { "Element": { - "kind": "component", - "name": "Comp3", - "alias": null, - "stereotype": null, + "identity": { + "name": "Comp3", + "alias": null, + "stereotype": null, + "element_kind": "component", + "source_location": { + "file": "", + "line": 17 + } + }, "style": null, - "statements": [], - "source_location": { - "file": "", - "line": 17 - } + "statements": [] } }, { "Element": { - "kind": "component", - "name": "Last\\ncomponent", - "alias": "Comp4", - "stereotype": null, + "identity": { + "name": "Last\\ncomponent", + "alias": "Comp4", + "stereotype": null, + "element_kind": "component", + "source_location": { + "file": "", + "line": 18 + } + }, "style": null, - "statements": [], - "source_location": { - "file": "", - "line": 18 - } + "statements": [] } } ] diff --git a/plantuml/parser/integration_test/component_diagram/plantuml/hide_or_remove_unlinked_component/output.json b/plantuml/parser/integration_test/component_diagram/plantuml/hide_or_remove_unlinked_component/output.json index ab2428e4..1549ef2f 100644 --- a/plantuml/parser/integration_test/component_diagram/plantuml/hide_or_remove_unlinked_component/output.json +++ b/plantuml/parser/integration_test/component_diagram/plantuml/hide_or_remove_unlinked_component/output.json @@ -4,44 +4,50 @@ "statements": [ { "Element": { - "kind": "component", - "name": "ActiveController", - "alias": null, - "stereotype": null, + "identity": { + "name": "ActiveController", + "alias": null, + "stereotype": null, + "element_kind": "component", + "source_location": { + "file": "", + "line": 14 + } + }, "style": null, - "statements": [], - "source_location": { - "file": "", - "line": 14 - } + "statements": [] } }, { "Element": { - "kind": "component", - "name": "DiagnosticsPanel", - "alias": null, - "stereotype": null, + "identity": { + "name": "DiagnosticsPanel", + "alias": null, + "stereotype": null, + "element_kind": "component", + "source_location": { + "file": "", + "line": 15 + } + }, "style": null, - "statements": [], - "source_location": { - "file": "", - "line": 15 - } + "statements": [] } }, { "Element": { - "kind": "component", - "name": "DormantSimulator", - "alias": null, - "stereotype": null, + "identity": { + "name": "DormantSimulator", + "alias": null, + "stereotype": null, + "element_kind": "component", + "source_location": { + "file": "", + "line": 16 + } + }, "style": null, - "statements": [], - "source_location": { - "file": "", - "line": 16 - } + "statements": [] } }, { diff --git a/plantuml/parser/integration_test/component_diagram/plantuml/individual_colors/output.json b/plantuml/parser/integration_test/component_diagram/plantuml/individual_colors/output.json index d592b794..01f60b23 100644 --- a/plantuml/parser/integration_test/component_diagram/plantuml/individual_colors/output.json +++ b/plantuml/parser/integration_test/component_diagram/plantuml/individual_colors/output.json @@ -4,19 +4,21 @@ "statements": [ { "Element": { - "kind": "component", - "name": "Trace Collector", - "alias": null, - "stereotype": null, + "identity": { + "name": "Trace Collector", + "alias": null, + "stereotype": null, + "element_kind": "component", + "source_location": { + "file": "", + "line": 14 + } + }, "style": { "color": null, "attributes": [] }, - "statements": [], - "source_location": { - "file": "", - "line": 14 - } + "statements": [] } } ] diff --git a/plantuml/parser/integration_test/component_diagram/plantuml/interfaces/output.json b/plantuml/parser/integration_test/component_diagram/plantuml/interfaces/output.json index cae6e3f7..f5917ee0 100644 --- a/plantuml/parser/integration_test/component_diagram/plantuml/interfaces/output.json +++ b/plantuml/parser/integration_test/component_diagram/plantuml/interfaces/output.json @@ -4,72 +4,82 @@ "statements": [ { "Element": { - "kind": "interface", - "name": "\"Command Intake\"", - "alias": null, - "stereotype": null, + "identity": { + "name": "\"Command Intake\"", + "alias": null, + "stereotype": null, + "element_kind": "interface", + "source_location": { + "file": "", + "line": 15 + } + }, "style": null, - "statements": [], - "source_location": { - "file": "", - "line": 15 - } + "statements": [] } }, { "Element": { - "kind": "interface", - "name": "\"Status Broadcast\"", - "alias": "StatusOut", - "stereotype": null, + "identity": { + "name": "\"Status Broadcast\"", + "alias": "StatusOut", + "stereotype": null, + "element_kind": "interface", + "source_location": { + "file": "", + "line": 16 + } + }, "style": null, - "statements": [], - "source_location": { - "file": "", - "line": 16 - } + "statements": [] } }, { "Element": { - "kind": "interface", - "name": "MaintenanceHook", - "alias": null, - "stereotype": null, + "identity": { + "name": "MaintenanceHook", + "alias": null, + "stereotype": null, + "element_kind": "interface", + "source_location": { + "file": "", + "line": 17 + } + }, "style": null, - "statements": [], - "source_location": { - "file": "", - "line": 17 - } + "statements": [] } }, { "Element": { - "kind": "interface", - "name": "Safety\\nfeedback", - "alias": "SafetyFeedback", - "stereotype": null, + "identity": { + "name": "Safety\\nfeedback", + "alias": "SafetyFeedback", + "stereotype": null, + "element_kind": "interface", + "source_location": { + "file": "", + "line": 18 + } + }, "style": null, - "statements": [], - "source_location": { - "file": "", - "line": 18 - } + "statements": [] } }, { "Element": { - "kind": "component", - "name": "telemetry_gateway", - "alias": null, - "stereotype": null, + "identity": { + "name": "telemetry_gateway", + "alias": null, + "stereotype": null, + "element_kind": "component", + "source_location": { + "file": "", + "line": 20 + } + }, "style": null, - "statements": [], - "source_location": { - "file": "", - "line": 20 - } + "statements": [] } } ] diff --git a/plantuml/parser/integration_test/component_diagram/plantuml/long_description/output.json b/plantuml/parser/integration_test/component_diagram/plantuml/long_description/output.json index e408c122..a554b3a3 100644 --- a/plantuml/parser/integration_test/component_diagram/plantuml/long_description/output.json +++ b/plantuml/parser/integration_test/component_diagram/plantuml/long_description/output.json @@ -4,16 +4,18 @@ "statements": [ { "Element": { - "kind": "component", - "name": "diagnostic_bundle", - "alias": null, - "stereotype": null, + "identity": { + "name": "diagnostic_bundle", + "alias": null, + "stereotype": null, + "element_kind": "component", + "source_location": { + "file": "", + "line": 14 + } + }, "style": null, - "statements": [], - "source_location": { - "file": "", - "line": 14 - } + "statements": [] } } ] diff --git a/plantuml/parser/integration_test/component_diagram/plantuml/use_uml2_notation/output.json b/plantuml/parser/integration_test/component_diagram/plantuml/use_uml2_notation/output.json index 68ca0e0e..20474edb 100644 --- a/plantuml/parser/integration_test/component_diagram/plantuml/use_uml2_notation/output.json +++ b/plantuml/parser/integration_test/component_diagram/plantuml/use_uml2_notation/output.json @@ -4,16 +4,18 @@ "statements": [ { "Element": { - "kind": "interface", - "name": "Telemetry Feed", - "alias": "Feed", - "stereotype": null, + "identity": { + "name": "Telemetry Feed", + "alias": "Feed", + "stereotype": null, + "element_kind": "interface", + "source_location": { + "file": "", + "line": 15 + } + }, "style": null, - "statements": [], - "source_location": { - "file": "", - "line": 15 - } + "statements": [] } }, { 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 a14a7e52..a17a7006 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 @@ -10,7 +10,7 @@ // // SPDX-License-Identifier: Apache-2.0 // ******************************************************************************* -pub use parser_core::common_ast::Arrow; +pub use parser_core::common_ast::{Arrow, ElementIdentity}; use serde::{Deserialize, Serialize}; use source_location::SourceLocation; @@ -43,13 +43,9 @@ pub enum PortType { #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] pub struct Element { - pub kind: String, - pub name: Option, - pub alias: Option, - pub stereotype: Option, + pub identity: ElementIdentity, pub style: Option, pub statements: Vec, // For nested components - pub source_location: SourceLocation, } #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] 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 a018b0f2..13c77a3a 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,8 @@ use std::rc::Rc; use thiserror::Error; use crate::{ - Arrow, CompPumlDocument, ComponentStyle, Element, Port, PortType, Relation, Statement, + Arrow, CompPumlDocument, ComponentStyle, Element, ElementIdentity, Port, PortType, Relation, + Statement, }; use parser_core::{ format_parse_tree, pest_to_syntax_error, BaseParseError, DiagramParser, ErrorLocation, @@ -135,12 +136,19 @@ impl PumlComponentParser { pair: pest::iterators::Pair, source_file: &str, ) -> Result { + let source_location = SourceLocation::new(source_file, pair.line_col().0 as u32); + let mut kind = String::new(); + let mut name: Option = None; + let mut alias: Option = None; + let mut stereotype: Option = None; let mut element = Element { - kind: "".to_string(), - name: None, - alias: None, - source_location: SourceLocation::new(source_file, pair.line_col().0 as u32), - stereotype: None, + identity: ElementIdentity { + name: None, + alias: None, + stereotype: None, + element_kind: String::new(), + source_location: source_location.clone(), + }, style: None, statements: Vec::new(), }; @@ -152,41 +160,42 @@ impl PumlComponentParser { for nested_inner in inner.into_inner() { match nested_inner.as_rule() { Rule::default_element => { - let (kind, name_opt) = Self::parse_default_element(nested_inner)?; - element.kind = kind; - element.name = name_opt; + let (parsed_kind, name_opt) = + Self::parse_default_element(nested_inner)?; + kind = parsed_kind; + 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; + kind = "component".to_string(); + name = name_opt; } _ => {} } } } Rule::short_form_component => { - element.name = Some(Self::extract_component_name(inner)); - element.kind = "component".to_string(); + name = Some(Self::extract_component_name(inner)); + kind = "component".to_string(); } Rule::short_form_actor => { - element.name = Some(Self::extract_actor_name(inner)); - element.kind = "actor".to_string(); + name = Some(Self::extract_actor_name(inner)); + kind = "actor".to_string(); } Rule::short_form_interface => { - element.name = Some(Self::extract_interface_name(inner)); - element.kind = "interface".to_string(); + name = Some(Self::extract_interface_name(inner)); + kind = "interface".to_string(); } Rule::short_form_usecase => { - element.name = Some(Self::extract_usecase_name(inner)); - element.kind = "usecase".to_string(); + name = Some(Self::extract_usecase_name(inner)); + kind = "usecase".to_string(); } Rule::alias_clause => { - element.alias = Self::extract_alias(inner); + alias = Self::extract_alias(inner); } Rule::stereotype => { - element.stereotype = Self::extract_stereotype(inner); + stereotype = Self::extract_stereotype(inner); } Rule::element_style => { element.style = Some(Self::parse_component_style(inner)?); @@ -198,6 +207,14 @@ impl PumlComponentParser { } } + element.identity = ElementIdentity { + name, + alias, + stereotype, + element_kind: kind, + source_location, + }; + Ok(element) } @@ -510,9 +527,9 @@ mod dispatch_style_tests { ), }; - assert_eq!(first_element.source_location.line, 2); + assert_eq!(first_element.identity.source_location.line, 2); assert_eq!( - first_element.source_location.file.as_ref(), + first_element.identity.source_location.file.as_ref(), expected_file.as_str() ); 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 f006410d..95bd5b60 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,7 @@ mod component_ast; mod component_parser; pub use component_ast::{ - Arrow, CompPumlDocument, ComponentStyle, Element, Port, PortType, Relation, Statement, + Arrow, CompPumlDocument, ComponentStyle, Element, ElementIdentity, Port, PortType, Relation, + Statement, }; pub use component_parser::{ComponentError, PumlComponentParser}; diff --git a/plantuml/parser/puml_parser/src/parser_core/BUILD b/plantuml/parser/puml_parser/src/parser_core/BUILD index 61f0830a..3613b7ea 100644 --- a/plantuml/parser/puml_parser/src/parser_core/BUILD +++ b/plantuml/parser/puml_parser/src/parser_core/BUILD @@ -31,6 +31,7 @@ rust_library( visibility = ["//plantuml/parser:__subpackages__"], deps = [ "//plantuml/parser/puml_utils", + "//tools/metamodel/common:source_location", "@crates//:log", "@crates//:pest", "@crates//:serde", diff --git a/plantuml/parser/puml_parser/src/parser_core/src/common_ast.rs b/plantuml/parser/puml_parser/src/parser_core/src/common_ast.rs index c0f6515f..b24c378e 100644 --- a/plantuml/parser/puml_parser/src/parser_core/src/common_ast.rs +++ b/plantuml/parser/puml_parser/src/parser_core/src/common_ast.rs @@ -11,6 +11,7 @@ // SPDX-License-Identifier: Apache-2.0 // ******************************************************************************* use serde::{Deserialize, Serialize}; +use source_location::SourceLocation; use std::default::Default; //////////////////////////////////////////////////////////////////////////////// @@ -61,3 +62,13 @@ pub enum ArrowDirection { Left, Right, } + +// ---------- Shared Raw Element Identity ---------- +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] +pub struct ElementIdentity { + pub name: Option, // text before "as", stripped of quotes + pub alias: Option, // text after "as" + pub stereotype: Option, // inside << >>, stripped of delimiters + pub element_kind: String, // keyword verbatim: "component","class","participant"… + pub source_location: SourceLocation, +} 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 69690a36..7de1cdd7 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 @@ -778,9 +778,10 @@ impl ComponentResolver { fn visit_element(&mut self, element: &Element) -> Result<(), ComponentResolverError> { let local_id = element + .identity .alias .as_deref() - .or(element.name.as_deref()) + .or(element.identity.name.as_deref()) .expect("Element must have name or alias (guaranteed by grammar)"); let fqn = self.make_fqn(local_id); @@ -796,12 +797,12 @@ impl ComponentResolver { let logic = LogicComponent { id: fqn.clone(), - name: element.name.clone(), - alias: element.alias.clone(), - source_location: element.source_location.clone(), + name: element.identity.name.clone(), + alias: element.identity.alias.clone(), + source_location: element.identity.source_location.clone(), parent_id: parent_id.clone(), - element_type: parse_kind(&element.kind)?, - stereotype: element.stereotype.clone(), + element_type: parse_kind(&element.identity.element_kind)?, + stereotype: element.identity.stereotype.clone(), relations: Vec::new(), }; 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 39278419..da96037e 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 @@ -263,6 +263,7 @@ fn test_nested_elements() { fn test_source_locations_are_preserved() { use component_diagram::SourceLocation; use component_parser::{CompPumlDocument, Element, Relation, Statement}; + use parser_core::common_ast::ElementIdentity; use parser_core::common_ast::{Arrow, ArrowLine}; let component_location = SourceLocation::new("input.puml", 2); @@ -272,22 +273,26 @@ fn test_source_locations_are_preserved() { name: None, statements: vec![ Statement::Element(Element { - kind: "component".to_string(), - name: Some("A".to_string()), - alias: None, - stereotype: None, + identity: ElementIdentity { + name: Some("A".to_string()), + alias: None, + stereotype: None, + element_kind: "component".to_string(), + source_location: component_location.clone(), + }, style: None, statements: Vec::new(), - source_location: component_location.clone(), }), Statement::Element(Element { - kind: "component".to_string(), - name: Some("B".to_string()), - alias: None, - stereotype: None, + identity: ElementIdentity { + name: Some("B".to_string()), + alias: None, + stereotype: None, + element_kind: "component".to_string(), + source_location: SourceLocation::new("input.puml", 3), + }, style: None, statements: Vec::new(), - source_location: SourceLocation::new("input.puml", 3), }), Statement::Relation(Relation { lhs: "A".to_string(),