use std::collections::BTreeSet;
use serde::{Deserialize, Serialize};
use crate::core::{
Binding, BindingEndpoint, BindingId, Edge, EdgeId, GraphId, GraphLocalBindingTarget, GroupId,
Node, NodeId, Port, PortId, StickyNoteId, SymbolId,
};
use super::{batch::GraphTransaction, endpoints::EdgeEndpoints, op::GraphOp};
fn is_false(value: &bool) -> bool {
!*value
}
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
pub struct GraphMutationFootprint {
#[serde(default, skip_serializing_if = "is_false")]
pub graph: bool,
#[serde(default, skip_serializing_if = "BTreeSet::is_empty")]
pub imports: BTreeSet<GraphId>,
#[serde(default, skip_serializing_if = "BTreeSet::is_empty")]
pub symbols: BTreeSet<SymbolId>,
#[serde(default, skip_serializing_if = "BTreeSet::is_empty")]
pub nodes: BTreeSet<NodeId>,
#[serde(default, skip_serializing_if = "BTreeSet::is_empty")]
pub ports: BTreeSet<PortId>,
#[serde(default, skip_serializing_if = "BTreeSet::is_empty")]
pub edges: BTreeSet<EdgeId>,
#[serde(default, skip_serializing_if = "BTreeSet::is_empty")]
pub groups: BTreeSet<GroupId>,
#[serde(default, skip_serializing_if = "BTreeSet::is_empty")]
pub sticky_notes: BTreeSet<StickyNoteId>,
#[serde(default, skip_serializing_if = "BTreeSet::is_empty")]
pub bindings: BTreeSet<BindingId>,
}
impl GraphMutationFootprint {
pub fn new() -> Self {
Self::default()
}
pub fn is_empty(&self) -> bool {
!self.graph
&& self.imports.is_empty()
&& self.symbols.is_empty()
&& self.nodes.is_empty()
&& self.ports.is_empty()
&& self.edges.is_empty()
&& self.groups.is_empty()
&& self.sticky_notes.is_empty()
&& self.bindings.is_empty()
}
pub fn extend(&mut self, other: Self) {
self.graph |= other.graph;
self.imports.extend(other.imports);
self.symbols.extend(other.symbols);
self.nodes.extend(other.nodes);
self.ports.extend(other.ports);
self.edges.extend(other.edges);
self.groups.extend(other.groups);
self.sticky_notes.extend(other.sticky_notes);
self.bindings.extend(other.bindings);
}
pub fn touch_graph(&mut self) {
self.graph = true;
}
pub fn touch_node(&mut self, id: NodeId) {
self.nodes.insert(id);
}
pub fn touch_port(&mut self, id: PortId) {
self.ports.insert(id);
}
pub fn touch_edge(&mut self, id: EdgeId) {
self.edges.insert(id);
}
pub fn touch_group(&mut self, id: GroupId) {
self.groups.insert(id);
}
pub fn touch_sticky_note(&mut self, id: StickyNoteId) {
self.sticky_notes.insert(id);
}
pub fn touch_binding(&mut self, id: BindingId) {
self.bindings.insert(id);
}
pub fn touch_import(&mut self, id: GraphId) {
self.imports.insert(id);
}
pub fn touch_symbol(&mut self, id: SymbolId) {
self.symbols.insert(id);
}
pub fn touch_node_snapshot(&mut self, id: NodeId, node: &Node) {
self.touch_node(id);
self.ports.extend(node.ports.iter().copied());
if let Some(parent) = node.parent {
self.touch_group(parent);
}
}
pub fn touch_port_snapshot(&mut self, id: PortId, port: &Port) {
self.touch_port(id);
self.touch_node(port.node);
}
pub fn touch_edge_snapshot(&mut self, id: EdgeId, edge: &Edge) {
self.touch_edge(id);
self.touch_edge_endpoints(EdgeEndpoints::from_edge(edge));
}
pub fn touch_edge_endpoints(&mut self, endpoints: EdgeEndpoints) {
self.touch_port(endpoints.from);
self.touch_port(endpoints.to);
}
pub fn touch_binding_snapshot(&mut self, id: BindingId, binding: &Binding) {
self.touch_binding(id);
self.touch_binding_endpoint(&binding.subject);
self.touch_binding_endpoint(&binding.target);
}
pub fn touch_binding_endpoint(&mut self, endpoint: &BindingEndpoint) {
let Some(target) = endpoint.graph_local_target() else {
return;
};
match target {
GraphLocalBindingTarget::Graph => self.touch_graph(),
GraphLocalBindingTarget::Node { id } => self.touch_node(id),
GraphLocalBindingTarget::Port { id } => self.touch_port(id),
GraphLocalBindingTarget::Edge { id } => self.touch_edge(id),
GraphLocalBindingTarget::Group { id } => self.touch_group(id),
GraphLocalBindingTarget::StickyNote { id } => self.touch_sticky_note(id),
}
}
}
impl GraphOp {
pub fn footprint(&self) -> GraphMutationFootprint {
let mut footprint = GraphMutationFootprint::new();
self.append_footprint(&mut footprint);
footprint
}
pub fn append_footprint(&self, footprint: &mut GraphMutationFootprint) {
match self {
Self::AddNode { id, node } | Self::RemoveNode { id, node, .. } => {
footprint.touch_node_snapshot(*id, node);
}
Self::SetNodePos { id, .. }
| Self::SetNodeOrigin { id, .. }
| Self::SetNodeKind { id, .. }
| Self::SetNodeKindVersion { id, .. }
| Self::SetNodeSelectable { id, .. }
| Self::SetNodeFocusable { id, .. }
| Self::SetNodeDraggable { id, .. }
| Self::SetNodeConnectable { id, .. }
| Self::SetNodeDeletable { id, .. }
| Self::SetNodeExtent { id, .. }
| Self::SetNodeExpandParent { id, .. }
| Self::SetNodeSize { id, .. }
| Self::SetNodeHidden { id, .. }
| Self::SetNodeCollapsed { id, .. }
| Self::SetNodeData { id, .. } => {
footprint.touch_node(*id);
}
Self::SetNodeParent { id, from, to } => {
footprint.touch_node(*id);
if let Some(parent) = from {
footprint.touch_group(*parent);
}
if let Some(parent) = to {
footprint.touch_group(*parent);
}
}
Self::SetNodePorts { id, from, to } => {
footprint.touch_node(*id);
footprint.ports.extend(from.iter().copied());
footprint.ports.extend(to.iter().copied());
}
Self::AddPort { id, port } | Self::RemovePort { id, port, .. } => {
footprint.touch_port_snapshot(*id, port);
}
Self::SetPortConnectable { id, .. }
| Self::SetPortConnectableStart { id, .. }
| Self::SetPortConnectableEnd { id, .. }
| Self::SetPortType { id, .. }
| Self::SetPortData { id, .. } => {
footprint.touch_port(*id);
}
Self::AddEdge { id, edge } | Self::RemoveEdge { id, edge, .. } => {
footprint.touch_edge_snapshot(*id, edge);
}
Self::SetEdgeKind { id, .. }
| Self::SetEdgeSelectable { id, .. }
| Self::SetEdgeFocusable { id, .. }
| Self::SetEdgeHidden { id, .. }
| Self::SetEdgeInteractionWidth { id, .. }
| Self::SetEdgeDeletable { id, .. }
| Self::SetEdgeReconnectable { id, .. }
| Self::SetEdgeData { id, .. }
| Self::SetEdgeView { id, .. } => {
footprint.touch_edge(*id);
}
Self::SetEdgeEndpoints { id, from, to } => {
footprint.touch_edge(*id);
footprint.touch_edge_endpoints(*from);
footprint.touch_edge_endpoints(*to);
}
Self::AddImport { id, .. }
| Self::RemoveImport { id, .. }
| Self::SetImportAlias { id, .. } => {
footprint.touch_import(*id);
}
Self::AddSymbol { id, .. }
| Self::RemoveSymbol { id, .. }
| Self::SetSymbolName { id, .. }
| Self::SetSymbolType { id, .. }
| Self::SetSymbolDefaultValue { id, .. }
| Self::SetSymbolMeta { id, .. } => {
footprint.touch_symbol(*id);
}
Self::AddGroup { id, .. } | Self::SetGroupRect { id, .. } => {
footprint.touch_group(*id);
}
Self::RemoveGroup {
id,
detached,
bindings,
..
} => {
footprint.touch_group(*id);
for (node, previous_parent) in detached {
footprint.touch_node(*node);
if let Some(previous_parent) = previous_parent {
footprint.touch_group(*previous_parent);
}
}
for (binding_id, binding) in bindings {
footprint.touch_binding_snapshot(*binding_id, binding);
}
}
Self::SetGroupTitle { id, .. } | Self::SetGroupColor { id, .. } => {
footprint.touch_group(*id);
}
Self::AddStickyNote { id, .. }
| Self::SetStickyNoteText { id, .. }
| Self::SetStickyNoteRect { id, .. }
| Self::SetStickyNoteColor { id, .. } => {
footprint.touch_sticky_note(*id);
}
Self::RemoveStickyNote { id, bindings, .. } => {
footprint.touch_sticky_note(*id);
for (binding_id, binding) in bindings {
footprint.touch_binding_snapshot(*binding_id, binding);
}
}
Self::AddBinding { id, binding } | Self::RemoveBinding { id, binding } => {
footprint.touch_binding_snapshot(*id, binding);
}
Self::SetBindingSubject { id, from, to } | Self::SetBindingTarget { id, from, to } => {
footprint.touch_binding(*id);
footprint.touch_binding_endpoint(from);
footprint.touch_binding_endpoint(to);
}
Self::SetBindingKind { id, .. } | Self::SetBindingMeta { id, .. } => {
footprint.touch_binding(*id);
}
}
self.append_cascaded_footprint(footprint);
}
fn append_cascaded_footprint(&self, footprint: &mut GraphMutationFootprint) {
match self {
Self::RemoveNode {
ports,
edges,
bindings,
..
} => {
for (port_id, port) in ports {
footprint.touch_port_snapshot(*port_id, port);
}
for (edge_id, edge) in edges {
footprint.touch_edge_snapshot(*edge_id, edge);
}
for (binding_id, binding) in bindings {
footprint.touch_binding_snapshot(*binding_id, binding);
}
}
Self::RemovePort {
edges, bindings, ..
} => {
for (edge_id, edge) in edges {
footprint.touch_edge_snapshot(*edge_id, edge);
}
for (binding_id, binding) in bindings {
footprint.touch_binding_snapshot(*binding_id, binding);
}
}
Self::RemoveEdge { bindings, .. } => {
for (binding_id, binding) in bindings {
footprint.touch_binding_snapshot(*binding_id, binding);
}
}
_ => {}
}
}
}
impl GraphTransaction {
pub fn footprint(&self) -> GraphMutationFootprint {
let mut footprint = GraphMutationFootprint::new();
self.append_footprint(&mut footprint);
footprint
}
pub fn append_footprint(&self, footprint: &mut GraphMutationFootprint) {
for op in self.ops() {
op.append_footprint(footprint);
}
}
}