Skip to main content

jellyflow_runtime/runtime/binding/
query.rs

1use jellyflow_core::core::{
2    BindingEndpoint, BindingId, CanvasPoint, CanvasRect, EdgeId, GraphLocalBindingTarget, GroupId,
3    NodeId, StickyNoteId,
4};
5
6use crate::runtime::geometry::EdgePosition;
7
8/// Options controlling runtime binding queries.
9#[derive(Debug, Default, Clone, Copy, PartialEq)]
10pub struct BindingQueryOptions {
11    pub include_hidden: bool,
12    pub fallback_node_size: Option<jellyflow_core::core::CanvasSize>,
13}
14
15impl BindingQueryOptions {
16    pub fn include_hidden(mut self, include_hidden: bool) -> Self {
17        self.include_hidden = include_hidden;
18        self
19    }
20
21    pub fn with_fallback_node_size(
22        mut self,
23        fallback_node_size: Option<jellyflow_core::core::CanvasSize>,
24    ) -> Self {
25        self.fallback_node_size = fallback_node_size;
26        self
27    }
28}
29
30/// Store-level binding facts derived from graph data and runtime geometry.
31#[derive(Debug, Clone, PartialEq)]
32pub struct BindingQueryResult {
33    pub revision: u64,
34    pub bindings: Vec<ResolvedBinding>,
35}
36
37impl BindingQueryResult {
38    pub fn new(revision: u64, bindings: Vec<ResolvedBinding>) -> Self {
39        Self { revision, bindings }
40    }
41
42    pub fn binding(&self, binding: BindingId) -> Option<&ResolvedBinding> {
43        self.bindings.iter().find(|resolved| resolved.id == binding)
44    }
45
46    pub fn pinned_node_ids(&self) -> impl Iterator<Item = NodeId> + '_ {
47        self.bindings
48            .iter()
49            .flat_map(|binding| [&binding.subject, &binding.target])
50            .filter_map(|endpoint| endpoint.pinnable_node())
51    }
52}
53
54/// One resolved binding relationship.
55#[derive(Debug, Clone, PartialEq)]
56pub struct ResolvedBinding {
57    pub id: BindingId,
58    pub subject: ResolvedBindingEndpoint,
59    pub target: ResolvedBindingEndpoint,
60    pub kind: Option<String>,
61}
62
63impl ResolvedBinding {
64    pub fn new(
65        id: BindingId,
66        subject: ResolvedBindingEndpoint,
67        target: ResolvedBindingEndpoint,
68        kind: Option<String>,
69    ) -> Self {
70        Self {
71            id,
72            subject,
73            target,
74            kind,
75        }
76    }
77}
78
79/// Resolved form of one binding endpoint.
80#[derive(Debug, Clone, PartialEq)]
81pub struct ResolvedBindingEndpoint {
82    pub endpoint: BindingEndpoint,
83    pub resolution: BindingEndpointResolution,
84}
85
86impl ResolvedBindingEndpoint {
87    pub fn new(endpoint: BindingEndpoint, resolution: BindingEndpointResolution) -> Self {
88        Self {
89            endpoint,
90            resolution,
91        }
92    }
93
94    pub fn unresolved(endpoint: BindingEndpoint) -> Self {
95        Self::new(endpoint, BindingEndpointResolution::Unresolved)
96    }
97
98    pub fn hidden(endpoint: BindingEndpoint) -> Self {
99        Self::new(endpoint, BindingEndpointResolution::Hidden)
100    }
101
102    pub fn source(endpoint: BindingEndpoint) -> Self {
103        Self::new(endpoint, BindingEndpointResolution::Source)
104    }
105
106    pub fn status(&self) -> BindingEndpointResolutionStatus {
107        self.resolution.status()
108    }
109
110    pub fn pinnable_node(&self) -> Option<NodeId> {
111        let BindingEndpoint::GraphLocal {
112            target: GraphLocalBindingTarget::Node { id },
113        } = self.endpoint
114        else {
115            return None;
116        };
117        self.resolution.is_resolved().then_some(id)
118    }
119}
120
121/// Geometry or status resolved for a binding endpoint.
122#[derive(Debug, Clone, Copy, PartialEq)]
123pub enum BindingEndpointResolution {
124    NodeRect {
125        node: NodeId,
126        rect: CanvasRect,
127        center: CanvasPoint,
128    },
129    PortAnchor {
130        node: NodeId,
131        point: CanvasPoint,
132    },
133    EdgePosition {
134        edge: EdgeId,
135        position: EdgePosition,
136    },
137    GroupRect {
138        group: GroupId,
139        rect: CanvasRect,
140        center: CanvasPoint,
141    },
142    StickyNoteRect {
143        note: StickyNoteId,
144        rect: CanvasRect,
145        center: CanvasPoint,
146    },
147    Graph,
148    Source,
149    Hidden,
150    Unresolved,
151}
152
153impl BindingEndpointResolution {
154    pub fn status(self) -> BindingEndpointResolutionStatus {
155        match self {
156            Self::NodeRect { .. }
157            | Self::PortAnchor { .. }
158            | Self::EdgePosition { .. }
159            | Self::GroupRect { .. }
160            | Self::StickyNoteRect { .. }
161            | Self::Graph
162            | Self::Source => BindingEndpointResolutionStatus::Resolved,
163            Self::Hidden => BindingEndpointResolutionStatus::Hidden,
164            Self::Unresolved => BindingEndpointResolutionStatus::Unresolved,
165        }
166    }
167
168    pub fn is_resolved(self) -> bool {
169        matches!(self.status(), BindingEndpointResolutionStatus::Resolved)
170    }
171}
172
173/// Coarse endpoint resolution state.
174#[derive(Debug, Clone, Copy, PartialEq, Eq)]
175pub enum BindingEndpointResolutionStatus {
176    Resolved,
177    Hidden,
178    Unresolved,
179}