Model typed SA ports and atomic exchange allocation workflows

This commit is contained in:
2026-09-05 18:22:00 +03:00
parent c20a2073d7
commit 0d14438b7b
13 changed files with 2402 additions and 256 deletions
+73 -15
View File
@@ -45,10 +45,20 @@ pub fn visit_element_refs(e: &mut Element, mut visit: impl FnMut(&mut Id)) {
visit(id);
}
}
SharedBody::Port {
interface: Some(id),
..
} => visit(id),
SharedBody::Port { contract, legacy } => {
match contract {
PortContract::FunctionInput { incoming_items } => {
incoming_items.iter_mut().for_each(&mut visit)
}
PortContract::FunctionOutput { outgoing_items } => {
outgoing_items.iter_mut().for_each(&mut visit)
}
_ => {}
}
if let Some(id) = legacy.as_mut().and_then(|old| old.interface.as_mut()) {
visit(id);
}
}
SharedBody::Scenario { context, steps, .. } => {
if let Some(id) = context {
visit(id);
@@ -145,7 +155,7 @@ pub fn visit_relation_refs(r: &mut Relation, mut visit: impl FnMut(&mut Id)) {
items,
..
} => {
for id in [source_port, target_port].into_iter().flatten() {
for id in [source_port, target_port] {
visit(id);
}
for id in items {
@@ -207,7 +217,49 @@ pub fn deletion_impact(p: &Project, id: Id) -> BTreeSet<Id> {
}
}
for r in p.relations.values() {
if removed.contains(&r.source()) || removed.contains(&r.target()) {
let port_removed = matches!(&r.body, RelationBody::FunctionalExchange { source_port, target_port, .. } if removed.contains(source_port) || removed.contains(target_port));
let work_allocation_removed = r.kind() == RelationKind::PortAllocation
&& p.relations.values().any(|allocation| {
allocation.kind() == RelationKind::Allocation
&& removed.contains(&allocation.id)
&& p.elements.get(&r.source()).and_then(|e| e.parent)
== Some(allocation.source())
&& p.elements.get(&r.target()).and_then(|e| e.parent)
== Some(allocation.target())
});
if removed.contains(&r.source())
|| removed.contains(&r.target())
|| port_removed
|| work_allocation_removed
{
removed.insert(r.id);
}
}
// Exchange allocation depends on both explicit endpoint allocations.
for r in p
.relations
.values()
.filter(|r| r.kind() == RelationKind::ExchangeAllocation)
{
let (Some(boundary), Some(work)) =
(p.relations.get(&r.source()), p.relations.get(&r.target()))
else {
continue;
};
let RelationBody::FunctionalExchange {
source_port,
target_port,
..
} = &work.body
else {
continue;
};
if p.relations.values().any(|allocation| {
allocation.kind() == RelationKind::PortAllocation
&& removed.contains(&allocation.id)
&& [boundary.source(), boundary.target()].contains(&allocation.source())
&& [*source_port, *target_port].contains(&allocation.target())
}) {
removed.insert(r.id);
}
}
@@ -263,7 +315,20 @@ pub fn delete_cascade(p: &mut Project, id: Id) {
SharedBody::Event {
body: EventBody::Signal { items },
} => items.retain(|id| !removed.contains(id)),
SharedBody::Port { interface, .. } => clear(interface),
SharedBody::Port { contract, legacy } => {
match contract {
PortContract::FunctionInput { incoming_items } => {
incoming_items.retain(|id| !removed.contains(id))
}
PortContract::FunctionOutput { outgoing_items } => {
outgoing_items.retain(|id| !removed.contains(id))
}
_ => {}
}
if let Some(old) = legacy {
clear(&mut old.interface);
}
}
SharedBody::Scenario { context, .. } => clear(context),
SharedBody::Lifeline { represented } => clear(represented),
SharedBody::Constraint { constrained, .. } => {
@@ -336,15 +401,8 @@ pub fn delete_cascade(p: &mut Project, id: Id) {
| RelationBody::BoundaryExchange { items, .. } => {
items.retain(|id| !removed.contains(id))
}
RelationBody::FunctionalExchange {
items,
source_port,
target_port,
..
} => {
RelationBody::FunctionalExchange { items, .. } => {
items.retain(|id| !removed.contains(id));
clear(source_port);
clear(target_port);
}
_ => {}
}