Complete source 6 engineering workflows and preserve release validation evidence
This commit is contained in:
@@ -6,6 +6,7 @@ use std::collections::BTreeSet;
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum Tool {
|
||||
Interaction,
|
||||
CommunicationMean,
|
||||
FunctionalExchange,
|
||||
BoundaryExchange,
|
||||
}
|
||||
@@ -13,7 +14,7 @@ pub enum Tool {
|
||||
impl Tool {
|
||||
pub fn for_layer(layer: Layer) -> &'static [Self] {
|
||||
match layer {
|
||||
Layer::Oa => &[Self::Interaction],
|
||||
Layer::Oa => &[Self::Interaction, Self::CommunicationMean],
|
||||
Layer::Sa => &[Self::FunctionalExchange, Self::BoundaryExchange],
|
||||
}
|
||||
}
|
||||
@@ -21,6 +22,7 @@ impl Tool {
|
||||
pub fn label(self) -> &'static str {
|
||||
match self {
|
||||
Self::Interaction => "Interaction",
|
||||
Self::CommunicationMean => "Communication means",
|
||||
Self::FunctionalExchange => "Functional exchange",
|
||||
Self::BoundaryExchange => "Boundary exchange",
|
||||
}
|
||||
@@ -29,6 +31,9 @@ impl Tool {
|
||||
pub fn hint(self) -> &'static str {
|
||||
match self {
|
||||
Self::Interaction => "Click a source activity, then a target activity.",
|
||||
Self::CommunicationMean => {
|
||||
"Click an operational entity or actor, then another participant. Communication is bidirectional unless you change its orientation."
|
||||
}
|
||||
Self::FunctionalExchange => {
|
||||
"Click an output port or source function, then an input port or target function. Function cards use automatic ports."
|
||||
}
|
||||
@@ -40,6 +45,9 @@ impl Tool {
|
||||
|
||||
pub fn can_start(self, project: &Project, source: Id) -> bool {
|
||||
match self {
|
||||
Self::CommunicationMean => project.elements.get(&source).is_some_and(|e| {
|
||||
e.layer() == Layer::Oa && matches!(e.kind(), Kind::Entity | Kind::Actor)
|
||||
}),
|
||||
Self::Interaction => project
|
||||
.elements
|
||||
.get(&source)
|
||||
@@ -57,6 +65,9 @@ impl Tool {
|
||||
return false;
|
||||
}
|
||||
match self {
|
||||
Self::CommunicationMean => {
|
||||
validation::can_connect(project, RelationKind::CommunicationMean, source, target)
|
||||
}
|
||||
Self::Interaction => {
|
||||
validation::can_connect(project, RelationKind::Interaction, source, target)
|
||||
}
|
||||
@@ -129,6 +140,9 @@ impl Tool {
|
||||
}
|
||||
let name = engineering_name(project, self, source, target);
|
||||
let body = match self {
|
||||
Self::CommunicationMean => {
|
||||
RelationBody::new(RelationKind::CommunicationMean, source, target)
|
||||
}
|
||||
Self::Interaction => RelationBody::new(RelationKind::Interaction, source, target),
|
||||
Self::BoundaryExchange => RelationBody::BoundaryExchange {
|
||||
source,
|
||||
@@ -210,7 +224,9 @@ fn engineering_name(project: &Project, tool: Tool, source: Id, target: Id) -> St
|
||||
project.named(id).to_owned()
|
||||
}
|
||||
};
|
||||
let arrow = if tool == Tool::BoundaryExchange && !boundary_oriented(project, source) {
|
||||
let arrow = if tool == Tool::CommunicationMean
|
||||
|| (tool == Tool::BoundaryExchange && !boundary_oriented(project, source))
|
||||
{
|
||||
"—"
|
||||
} else {
|
||||
"→"
|
||||
@@ -273,7 +289,10 @@ mod tests {
|
||||
let actor = element(&mut p, Kind::Actor, Layer::Oa, "Dispatcher");
|
||||
let function = element(&mut p, Kind::Function, Layer::Sa, "Assess report");
|
||||
let before = p.clone();
|
||||
assert_eq!(Tool::for_layer(Layer::Oa), &[Tool::Interaction]);
|
||||
assert_eq!(
|
||||
Tool::for_layer(Layer::Oa),
|
||||
&[Tool::Interaction, Tool::CommunicationMean]
|
||||
);
|
||||
assert_eq!(
|
||||
Tool::Interaction.targets(&p, source),
|
||||
BTreeSet::from([target])
|
||||
@@ -289,6 +308,27 @@ mod tests {
|
||||
assert_eq!(p, before);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn operational_communication_uses_participants_without_creating_ports() {
|
||||
let mut p = Project::empty();
|
||||
let a = element(&mut p, Kind::Entity, Layer::Oa, "Field team");
|
||||
let b = element(&mut p, Kind::Actor, Layer::Oa, "Coordinator");
|
||||
let activity = element(&mut p, Kind::Activity, Layer::Oa, "Report");
|
||||
let sa = element(&mut p, Kind::System, Layer::Sa, "Controller");
|
||||
let before = p.clone();
|
||||
assert_eq!(Tool::CommunicationMean.targets(&p, a), BTreeSet::from([b]));
|
||||
for invalid in [a, activity, sa, Id::new_v4()] {
|
||||
assert!(Tool::CommunicationMean.command(&p, a, invalid).is_err());
|
||||
}
|
||||
let command = Tool::CommunicationMean.command(&p, a, b).unwrap();
|
||||
let (accepted, relation) = created(&p, &command);
|
||||
assert!(matches!(relation.body, RelationBody::CommunicationMean {
|
||||
source, target, oriented: false, ref items,
|
||||
} if source == a && target == b && items.is_empty()));
|
||||
assert_eq!(accepted.elements, p.elements);
|
||||
assert_eq!(p, before);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn explicit_function_ports_keep_owners_contracts_and_do_not_invent_carried_items() {
|
||||
let mut p = Project::empty();
|
||||
@@ -532,6 +572,8 @@ mod tests {
|
||||
let mut p = Project::empty();
|
||||
let oa_a = element(&mut p, Kind::Activity, Layer::Oa, "Detect incident");
|
||||
let oa_b = element(&mut p, Kind::Activity, Layer::Oa, "Assess incident");
|
||||
let oa_team = element(&mut p, Kind::Entity, Layer::Oa, "Response team");
|
||||
let oa_actor = element(&mut p, Kind::Actor, Layer::Oa, "Dispatcher");
|
||||
let sa_a = element(&mut p, Kind::Function, Layer::Sa, "Detect report");
|
||||
let sa_b = element(&mut p, Kind::Function, Layer::Sa, "Assess report");
|
||||
let system = element(&mut p, Kind::System, Layer::Sa, "System");
|
||||
@@ -547,6 +589,7 @@ mod tests {
|
||||
let mut document = Document::from_bundle(bundle, ProjectLimits::default()).unwrap();
|
||||
for (tool, source, target) in [
|
||||
(Tool::Interaction, oa_a, oa_b),
|
||||
(Tool::CommunicationMean, oa_team, oa_actor),
|
||||
(Tool::FunctionalExchange, sa_a, sa_b),
|
||||
(Tool::BoundaryExchange, boundary_a, boundary_b),
|
||||
] {
|
||||
|
||||
Reference in New Issue
Block a user