jellyflow_runtime/runtime/conformance/scenario/action/
layout_facts.rs1use serde::{Deserialize, Serialize};
2
3use crate::runtime::connection::{ConnectionHandleRef, ResolvedConnectionTarget};
4use crate::runtime::geometry::{EdgePosition, HandlePosition, ResolvedEdgeRouteKind};
5use crate::runtime::measurement::NodeMeasurement;
6use jellyflow_core::core::{CanvasPoint, CanvasSize, EdgeId, NodeId};
7
8use super::ConformanceAction;
9
10pub(super) fn kind(action: &ConformanceAction) -> Option<&'static str> {
11 Some(match action {
12 ConformanceAction::ReportNodeMeasurement { .. } => "report_node_measurement",
13 ConformanceAction::AssertLayoutFacts { .. } => "assert_layout_facts",
14 _ => return None,
15 })
16}
17
18#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
19pub struct ConformanceLayoutFactsExpectation {
20 #[serde(default, skip_serializing_if = "Vec::is_empty")]
21 pub visible_node_ids: Vec<NodeId>,
22 #[serde(default, skip_serializing_if = "Vec::is_empty")]
23 pub visible_edge_ids: Vec<EdgeId>,
24 #[serde(default, skip_serializing_if = "Vec::is_empty")]
25 pub edge_positions: Vec<ConformanceLayoutEdgePosition>,
26 #[serde(default, skip_serializing_if = "Vec::is_empty")]
27 pub edge_routes: Vec<ConformanceLayoutEdgeRouteFacts>,
28 #[serde(default, skip_serializing_if = "Option::is_none")]
29 pub connection_target: Option<ConformanceLayoutFactsConnectionTargetExpectation>,
30}
31
32impl ConformanceLayoutFactsExpectation {
33 pub fn new(
34 visible_node_ids: impl IntoIterator<Item = NodeId>,
35 visible_edge_ids: impl IntoIterator<Item = EdgeId>,
36 ) -> Self {
37 Self {
38 visible_node_ids: visible_node_ids.into_iter().collect(),
39 visible_edge_ids: visible_edge_ids.into_iter().collect(),
40 edge_positions: Vec::new(),
41 edge_routes: Vec::new(),
42 connection_target: None,
43 }
44 }
45
46 pub fn with_edge_positions(
47 mut self,
48 edge_positions: impl IntoIterator<Item = ConformanceLayoutEdgePosition>,
49 ) -> Self {
50 self.edge_positions = edge_positions.into_iter().collect();
51 self
52 }
53
54 pub fn with_edge_routes(
55 mut self,
56 edge_routes: impl IntoIterator<Item = ConformanceLayoutEdgeRouteFacts>,
57 ) -> Self {
58 self.edge_routes = edge_routes.into_iter().collect();
59 self
60 }
61
62 pub fn with_connection_target(
63 mut self,
64 connection_target: ConformanceLayoutFactsConnectionTargetExpectation,
65 ) -> Self {
66 self.connection_target = Some(connection_target);
67 self
68 }
69}
70
71#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
72pub struct ConformanceLayoutEdgePosition {
73 pub edge: EdgeId,
74 pub source: ConformanceEdgeEndpointPosition,
75 pub target: ConformanceEdgeEndpointPosition,
76}
77
78impl ConformanceLayoutEdgePosition {
79 pub fn new(
80 edge: EdgeId,
81 source: ConformanceEdgeEndpointPosition,
82 target: ConformanceEdgeEndpointPosition,
83 ) -> Self {
84 Self {
85 edge,
86 source,
87 target,
88 }
89 }
90
91 pub fn from_edge_position(edge: EdgeId, position: EdgePosition) -> Self {
92 Self {
93 edge,
94 source: ConformanceEdgeEndpointPosition::new(
95 position.source.point,
96 position.source.position,
97 ),
98 target: ConformanceEdgeEndpointPosition::new(
99 position.target.point,
100 position.target.position,
101 ),
102 }
103 }
104
105 pub(crate) fn matches_edge_position(self, position: EdgePosition) -> bool {
106 self.source.matches_endpoint(position.source)
107 && self.target.matches_endpoint(position.target)
108 }
109}
110
111#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
112pub struct ConformanceEdgeEndpointPosition {
113 pub point: CanvasPoint,
114 pub position: HandlePosition,
115}
116
117impl ConformanceEdgeEndpointPosition {
118 pub fn new(point: CanvasPoint, position: HandlePosition) -> Self {
119 Self { point, position }
120 }
121
122 fn matches_endpoint(self, endpoint: crate::runtime::geometry::EdgeEndpointPosition) -> bool {
123 self.point == endpoint.point && self.position == endpoint.position
124 }
125}
126
127#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
128pub struct ConformanceLayoutEdgeRouteFacts {
129 pub edge: EdgeId,
130 pub kind: ResolvedEdgeRouteKind,
131 pub interaction_width: f32,
132 pub selected: bool,
133}
134
135impl ConformanceLayoutEdgeRouteFacts {
136 pub fn new(
137 edge: EdgeId,
138 kind: ResolvedEdgeRouteKind,
139 interaction_width: f32,
140 selected: bool,
141 ) -> Self {
142 Self {
143 edge,
144 kind,
145 interaction_width,
146 selected,
147 }
148 }
149}
150
151#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
152pub struct ConformanceLayoutFactsConnectionTargetExpectation {
153 pub pointer: CanvasPoint,
154 pub from: ConnectionHandleRef,
155 pub expected: ResolvedConnectionTarget,
156}
157
158impl ConformanceLayoutFactsConnectionTargetExpectation {
159 pub fn new(
160 pointer: CanvasPoint,
161 from: ConnectionHandleRef,
162 expected: ResolvedConnectionTarget,
163 ) -> Self {
164 Self {
165 pointer,
166 from,
167 expected,
168 }
169 }
170}
171
172impl ConformanceAction {
173 pub fn report_node_measurement(measurement: NodeMeasurement) -> Self {
174 Self::ReportNodeMeasurement { measurement }
175 }
176
177 pub fn assert_layout_facts(
178 viewport_size: CanvasSize,
179 expected: ConformanceLayoutFactsExpectation,
180 ) -> Self {
181 Self::AssertLayoutFacts {
182 viewport_size,
183 expected,
184 }
185 }
186}