Skip to main content

datum/graph/
ports.rs

1//! Typed and type-erased graph ports.
2//!
3//! [`Inlet`]/[`Outlet`] are the typed handles a stage hands back through its
4//! [`Shape`](super::Shape); [`AnyInlet`]/[`AnyOutlet`] are their erased forms
5//! (carrying the element [`TypeId`] and a name) used by the builder for runtime
6//! wiring validation. A port is just an identity (`PortId` + name) plus a
7//! phantom element type — it stores no runtime state; per-materialization port
8//! state lives in [`GraphStageLogic`](super::GraphStageLogic).
9
10use super::*;
11
12/// A typed graph inlet: the downstream end of an edge that accepts `T` elements.
13///
14/// Cheap to clone (an id + an `Arc<str>` name). Call [`erase`](Self::erase) to
15/// get the type-erased [`AnyInlet`] the builder validates and wires against.
16#[derive(PartialEq, Eq, Hash)]
17pub struct Inlet<T: 'static> {
18    id: PortId,
19    name: Arc<str>,
20    _marker: PhantomData<fn() -> T>,
21}
22
23impl<T: 'static> Inlet<T> {
24    #[must_use]
25    pub fn new(name: impl Into<String>) -> Self {
26        Self::with_id(next_port_id(), name)
27    }
28
29    pub(super) fn with_id(id: PortId, name: impl Into<String>) -> Self {
30        Self::with_name(id, Arc::from(name.into()))
31    }
32
33    pub(super) fn with_arc_name(id: PortId, name: Arc<str>) -> Self {
34        Self::with_name(id, name)
35    }
36
37    fn with_name(id: PortId, name: Arc<str>) -> Self {
38        Self {
39            id,
40            name,
41            _marker: PhantomData,
42        }
43    }
44
45    #[must_use]
46    pub const fn id(&self) -> PortId {
47        self.id
48    }
49
50    #[must_use]
51    pub fn name(&self) -> &str {
52        &self.name
53    }
54
55    #[must_use]
56    pub fn erase(&self) -> AnyInlet {
57        AnyInlet {
58            id: self.id,
59            name: Arc::clone(&self.name),
60            type_id: TypeId::of::<T>(),
61            type_name: type_name::<T>(),
62        }
63    }
64}
65
66impl<T: 'static> Clone for Inlet<T> {
67    fn clone(&self) -> Self {
68        Self {
69            id: self.id,
70            name: Arc::clone(&self.name),
71            _marker: PhantomData,
72        }
73    }
74}
75
76impl<T: 'static> fmt::Debug for Inlet<T> {
77    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
78        f.debug_struct("Inlet")
79            .field("id", &self.id)
80            .field("name", &self.name)
81            .field("type", &type_name::<T>())
82            .finish()
83    }
84}
85
86/// A typed graph outlet: the upstream end of an edge that emits `T` elements.
87///
88/// Cheap to clone (an id + an `Arc<str>` name). Call [`erase`](Self::erase) to
89/// get the type-erased [`AnyOutlet`] the builder validates and wires against.
90#[derive(PartialEq, Eq, Hash)]
91pub struct Outlet<T: 'static> {
92    id: PortId,
93    name: Arc<str>,
94    _marker: PhantomData<fn() -> T>,
95}
96
97impl<T: 'static> Outlet<T> {
98    #[must_use]
99    pub fn new(name: impl Into<String>) -> Self {
100        Self::with_id(next_port_id(), name)
101    }
102
103    pub(super) fn with_id(id: PortId, name: impl Into<String>) -> Self {
104        Self::with_name(id, Arc::from(name.into()))
105    }
106
107    pub(super) fn with_arc_name(id: PortId, name: Arc<str>) -> Self {
108        Self::with_name(id, name)
109    }
110
111    fn with_name(id: PortId, name: Arc<str>) -> Self {
112        Self {
113            id,
114            name,
115            _marker: PhantomData,
116        }
117    }
118
119    #[must_use]
120    pub const fn id(&self) -> PortId {
121        self.id
122    }
123
124    #[must_use]
125    pub fn name(&self) -> &str {
126        &self.name
127    }
128
129    #[must_use]
130    pub fn erase(&self) -> AnyOutlet {
131        AnyOutlet {
132            id: self.id,
133            name: Arc::clone(&self.name),
134            type_id: TypeId::of::<T>(),
135            type_name: type_name::<T>(),
136        }
137    }
138}
139
140impl<T: 'static> Clone for Outlet<T> {
141    fn clone(&self) -> Self {
142        Self {
143            id: self.id,
144            name: Arc::clone(&self.name),
145            _marker: PhantomData,
146        }
147    }
148}
149
150impl<T: 'static> fmt::Debug for Outlet<T> {
151    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
152        f.debug_struct("Outlet")
153            .field("id", &self.id)
154            .field("name", &self.name)
155            .field("type", &type_name::<T>())
156            .finish()
157    }
158}
159
160/// Type-erased [`Inlet`] carrying the element `TypeId` and name.
161///
162/// Produced by [`Inlet::erase`]. The builder stores these and compares their
163/// `type_id` on `connect`/`wire` to validate that an edge joins ports of the
164/// same element type at runtime.
165#[derive(Clone, Debug, PartialEq, Eq, Hash)]
166pub struct AnyInlet {
167    id: PortId,
168    pub(super) name: Arc<str>,
169    type_id: TypeId,
170    type_name: &'static str,
171}
172
173impl AnyInlet {
174    #[must_use]
175    pub const fn id(&self) -> PortId {
176        self.id
177    }
178
179    #[must_use]
180    pub fn name(&self) -> &str {
181        &self.name
182    }
183
184    #[must_use]
185    pub const fn type_id(&self) -> TypeId {
186        self.type_id
187    }
188
189    #[must_use]
190    pub const fn type_name(&self) -> &'static str {
191        self.type_name
192    }
193}
194
195/// Type-erased [`Outlet`] carrying the element `TypeId` and name.
196///
197/// Produced by [`Outlet::erase`]. The builder stores these and compares their
198/// `type_id` on `connect`/`wire` to validate that an edge joins ports of the
199/// same element type at runtime.
200#[derive(Clone, Debug, PartialEq, Eq, Hash)]
201pub struct AnyOutlet {
202    id: PortId,
203    pub(super) name: Arc<str>,
204    type_id: TypeId,
205    type_name: &'static str,
206}
207
208impl AnyOutlet {
209    #[must_use]
210    pub const fn id(&self) -> PortId {
211        self.id
212    }
213
214    #[must_use]
215    pub fn name(&self) -> &str {
216        &self.name
217    }
218
219    #[must_use]
220    pub const fn type_id(&self) -> TypeId {
221        self.type_id
222    }
223
224    #[must_use]
225    pub const fn type_name(&self) -> &'static str {
226        self.type_name
227    }
228}
229
230/// Common read accessors over a typed port, independent of element type or
231/// direction. Implemented by both [`Inlet`] and [`Outlet`]; used by handler
232/// helpers (e.g. `set_handler`) that only need a port's id, name, and kind.
233pub trait PortRef {
234    fn id(&self) -> PortId;
235    fn name(&self) -> &str;
236    fn kind(&self) -> PortKind;
237}
238
239impl<T: 'static> PortRef for Inlet<T> {
240    fn id(&self) -> PortId {
241        self.id
242    }
243
244    fn name(&self) -> &str {
245        &self.name
246    }
247
248    fn kind(&self) -> PortKind {
249        PortKind::Inlet
250    }
251}
252
253impl<T: 'static> PortRef for Outlet<T> {
254    fn id(&self) -> PortId {
255        self.id
256    }
257
258    fn name(&self) -> &str {
259        &self.name
260    }
261
262    fn kind(&self) -> PortKind {
263        PortKind::Outlet
264    }
265}