Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions MODULE.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,10 @@ crate.spec(
package = "serde_yaml",
version = "0.9",
)
crate.spec(
package = "strsim",
version = "0.11",
)
crate.spec(
package = "once_cell",
version = "1.19",
Expand Down
1 change: 1 addition & 0 deletions validation/core/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ rust_library(
"@crates//:log",
"@crates//:serde",
"@crates//:serde_json",
"@crates//:strsim",
],
)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,5 @@ error_contains: |
Parent : component_example
Component source file : "validation/core/integration_test/bazel_component/negative_extra_unit/component_diagram.puml"
Component source line : 20
Suggestion : did you mean unit "unit_1"?
Fix : Add the corresponding Bazel unit definition for "unit_3", or remove it from the PlantUML component diagram.
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,5 @@ error_contains: |
Parent : component_example
Stereotype : <<unit>>
Bazel label : @//bazel/rules/rules_score/examples/seooc/unit_2:unit_2
Suggestion : did you mean unit "unit_1"?
Fix : Add unit "unit_2" with stereotype <<unit>> in the PlantUML component diagram, or remove it from Bazel.
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ filegroup(
"//validation/core/integration_test/component_internal_api/negative_duplicate_interface_alias_casefolded:case_data",
"//validation/core/integration_test/component_internal_api/negative_duplicate_unit_alias_casefolded:case_data",
"//validation/core/integration_test/component_internal_api/negative_interface_missing_from_internal_api:case_data",
"//validation/core/integration_test/component_internal_api/negative_interface_missing_from_internal_api_with_suggestion:case_data",
"//validation/core/integration_test/component_internal_api/positive_interface_match:case_data",
],
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,11 @@ fn negative_interface_missing_from_internal_api_suite_case() {
assert_case("negative_interface_missing_from_internal_api");
}

#[test]
fn negative_interface_missing_from_internal_api_with_suggestion_suite_case() {
assert_case("negative_interface_missing_from_internal_api_with_suggestion");
}

#[test]
fn negative_duplicate_unit_alias_casefolded_suite_case() {
assert_case("negative_duplicate_unit_alias_casefolded");
Expand Down
Original file line number Diff line number Diff line change
@@ -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
# *******************************************************************************

load("//bazel/rules/rules_score:rules_score.bzl", "architectural_design")
load("//validation/core/integration_test:puml_fixture.bzl", "provider_fbs_fixture_bundle")

architectural_design(
name = "design",
internal_api = ["internal_api_diagram.puml"],
maturity = "development",
static = ["component_diagram.puml"],
visibility = ["//visibility:private"],
)

provider_fbs_fixture_bundle(
name = "fbs",
visibility = ["//visibility:public"],
deps = [":design"],
)

filegroup(
name = "case_data",
srcs = [
"expected.yaml",
":fbs",
],
visibility = ["//visibility:public"],
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
' *******************************************************************************
' 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 component_diagram

package "Package A" as package_a {
component "Component A" as component_a <<component>> {
component "Unit 1" as unit_1 <<unit>>
component "Unit 2" as unit_2 <<unit>>
}

interface "InternalInterface" as InternalInterface
unit_1 -( InternalInterface
unit_2 )- InternalInterface
}

@enduml
Original file line number Diff line number Diff line change
@@ -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
# *******************************************************************************
should_pass: false
error_contains: |-
[Interface] Component interface(s) "package_a.InternalInterface" from the component diagram not found in the internal API diagram.
Missing interfaces : "package_a.InternalInterface"
Component source file for "package_a.InternalInterface" : "validation/core/integration_test/component_internal_api/negative_interface_missing_from_internal_api_with_suggestion/component_diagram.puml"
Component source line for "package_a.InternalInterface" : 22
Suggestion : did you mean interface "package_a.internal_interface"?
Fix : Add interface declaration(s) "package_a.InternalInterface" in the internal API diagram, or remove those interface declarations from the component diagram.
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
' *******************************************************************************
' 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

package package_a {
interface "internal_interface" as internal_interface {
+ GetData()
}
}

@enduml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ error_contains:
Alias : "unit_3"
Component source file : "validation/core/integration_test/component_sequence/negative_missing_participant/component_diagram.puml"
Component source line : 20
Suggestion : did you mean "unit_1"?
Fix : Add sequence participant "unit_3" in the sequence diagram, or remove it from the component diagram.
- |
[Interface] Sequence-connected units "unit_1" and "unit_2" have no corresponding shared interface connection in the component diagram.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,14 @@ error_contains:
Alias : "unit_1"
Component source file : "validation/core/integration_test/component_sequence/negative_mixed_mismatch/component_diagram.puml"
Component source line : 18
Suggestion : did you mean "unit_2"?
Fix : Add sequence participant "unit_1" in the sequence diagram, or remove it from the component diagram.
- |
[Naming] Participant "unit_3" from the sequence diagram not found in the component diagram.
Participant : "unit_3"
Sequence source file : "validation/core/integration_test/component_sequence/negative_mixed_mismatch/sequence_diagram.puml"
Sequence source line : 17
Suggestion : did you mean "unit_1"?
Fix : Add component unit alias "unit_3" in the component diagram, or remove it from the sequence diagram.
- |
[Interface] Sequence-connected units "unit_2" and "unit_3" have no corresponding shared interface connection in the component diagram.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ error_contains:
Participant : "unit_3"
Sequence source file : "validation/core/integration_test/component_sequence/negative_orphan_participant/sequence_diagram.puml"
Sequence source line : 17
Suggestion : did you mean "unit_1"?
Fix : Add component unit alias "unit_3" in the component diagram, or remove it from the sequence diagram.
- |
[Interface] Sequence-connected units "unit_1" and "unit_3" have no corresponding shared interface connection in the component diagram.
Expand Down
11 changes: 11 additions & 0 deletions validation/core/src/results/error_models.rs
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,17 @@ impl ErrorBuilder {
self
}

pub fn suggest(self, suggestion_subject: Option<&str>, suggested_name: &str) -> Self {
let suggestion_text = match suggestion_subject {
Some(suggestion_subject) => {
format!("did you mean {suggestion_subject} \"{suggested_name}\"?")
}
None => format!("did you mean \"{suggested_name}\"?"),
};

self.field("suggestion", suggestion_text)
}

pub fn build(self) -> String {
let label_width = self
.fields
Expand Down
43 changes: 39 additions & 4 deletions validation/core/src/validators/bazel_component_validator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@

use std::collections::{BTreeMap, BTreeSet};

use super::shared::best_string_suggestion;
use crate::models::{BazelArchitecture, ComponentDiagramArchitecture, LogicComponent};
use crate::results::{ErrorBuilder, ErrorCategory};
use crate::{Diagnostics, ValidationResult};
Expand Down Expand Up @@ -93,12 +94,20 @@ impl BazelComponentValidator {
if !diagram_set.contains_key(key) {
let (name, parent) = key;
let parent_str = Self::parent_display(parent, default_parent);
let suggested_alias = best_string_suggestion(
name,
diagram_set
.keys()
.filter(|(_, candidate_parent)| candidate_parent == parent)
.map(|(candidate_name, _)| candidate_name.as_str()),
);
self.result.add_failure(Self::format_missing(
display_type,
stereotype,
name,
&parent_str,
label,
suggested_alias.as_deref(),
));
}
}
Expand All @@ -108,11 +117,19 @@ impl BazelComponentValidator {
if !bazel_set.contains_key(key) {
let (name, parent) = key;
let parent_str = Self::parent_display(parent, default_parent);
let suggested_alias = best_string_suggestion(
name,
bazel_set
.keys()
.filter(|(_, candidate_parent)| candidate_parent == parent)
.map(|(candidate_name, _)| candidate_name.as_str()),
);
self.result.add_failure(Self::format_extra(
display_type,
name,
&parent_str,
entity,
suggested_alias.as_deref(),
));
}
}
Expand Down Expand Up @@ -176,15 +193,24 @@ impl BazelComponentValidator {
name: &str,
parent_str: &str,
label: &str,
suggested_alias: Option<&str>,
) -> String {
ErrorBuilder::new(ErrorCategory::Naming)
let error = ErrorBuilder::new(ErrorCategory::Naming)
.title(format!(
"{display_type} \"{name}\" from Bazel not found in the PlantUML component diagram"
))
.field("alias", format!("\"{name}\""))
.field("parent", parent_str)
.field("stereotype", format!("<<{stereotype}>>"))
.field("bazel label", label)
.field("bazel label", label);

let error = if let Some(suggested_alias) = suggested_alias {
error.suggest(Some(display_type), suggested_alias)
} else {
error
};

error
.fix(format!(
"add {display_type} \"{name}\" with stereotype <<{stereotype}>> in the PlantUML component diagram, or remove it from Bazel"
))
Expand All @@ -196,17 +222,26 @@ impl BazelComponentValidator {
name: &str,
parent_str: &str,
entity: &LogicComponent,
suggested_alias: Option<&str>,
) -> String {
let (source_file, source_line) = entity.source_location.display();

ErrorBuilder::new(ErrorCategory::Naming)
let error = ErrorBuilder::new(ErrorCategory::Naming)
.title(format!(
"{entity_type} \"{name}\" from the PlantUML component diagram not found in Bazel"
))
.field("alias", format!("\"{name}\""))
.field("parent", parent_str)
.field("component source file", format!("\"{source_file}\""))
.field("component source line", source_line.to_string())
.field("component source line", source_line.to_string());

let error = if let Some(suggested_alias) = suggested_alias {
error.suggest(Some(entity_type), suggested_alias)
} else {
error
};

error
.fix(format!(
"add the corresponding Bazel {entity_type} definition for \"{name}\", or remove it from the PlantUML component diagram"
))
Expand Down
Loading
Loading