hydro_lang/compile/embedded_runtime.rs
1//! Runtime helpers for the embedded deployment backend.
2//!
3//! This module is NOT gated on the `build` feature so that the staged
4//! re-exports are available at runtime in the generated code.
5
6use std::marker::PhantomData;
7
8use crate::live_collections::stream::{
9 AtLeastOnce, ExactlyOnce, NoOrder, Ordering, Retries, TotalOrder,
10};
11use crate::networking::NetworkingConfig;
12use crate::properties::ManualProof;
13
14/// A channel crossing the boundary of an embedded dataflow: a network channel end, a stream
15/// input, a membership stream, or an output, with its guarantees as marker types (`Ordering`
16/// and `Retries`).
17///
18/// - For a stream input or membership stream, the guarantees are what the dataflow relies on,
19/// which the host's [`Promises`] must meet.
20/// - For the sending end of a network channel, they are what the dataflow sends, which the
21/// host's [`Acknowledgements`] must cover, as for an output.
22/// - For the receiving end of a network channel, they are what the dataflow relies on: the sent
23/// stream, weakened by the channel's configuration. The host's [`Promises`] must meet them, as
24/// for a stream input.
25/// - For an output, they are what the dataflow provides. The host's [`Acknowledgements`] must
26/// cover every way the output may be weaker than ordered and exactly-once.
27///
28/// Each location's generated module has a marker type implementing this for each
29/// channel crossing it, named after its field: under `incoming` / `outgoing` for
30/// network channel ends, `inputs` for stream inputs, `membership` for membership streams, and
31/// `outputs` for outputs. Each field of the generated structs is an [`EmbeddedInputChannel`] (a stream
32/// input or membership stream), a [`EmbeddedNetworkInputChannel`] (a receiving end), a [`EmbeddedNetworkOutputChannel`] (a
33/// sending end), or an [`EmbeddedOutputChannel`] (an output) over its marker. The marker is inferred from
34/// the field. The markers of network channel ends also implement [`EmbeddedNetworkChannel`].
35pub trait EmbeddedChannel {
36 /// The ordering the host must keep (for an output or the sending end of a network channel,
37 /// the ordering the dataflow provides): `TotalOrder` or `NoOrder`. For a network channel,
38 /// `TotalOrder` means each sender's messages in the order it sent them; for an input, that
39 /// the stream's order is the order to process; for a membership stream, each member's events
40 /// in the order they happened.
41 type Ordering: Ordering;
42
43 /// The retry guarantee the host must keep (for an output or the sending end of a network
44 /// channel, the one the dataflow provides): `ExactlyOnce` (nothing delivered twice) or
45 /// `AtLeastOnce`.
46 type Retries: Retries;
47}
48
49/// What a host promises about something it supplies, each guarantee promised separately with
50/// a [`manual_proof!`](crate::prelude::manual_proof) saying why it holds. An [`EmbeddedInputChannel`] or
51/// [`EmbeddedNetworkInputChannel`] is built from a stream and the `Promises` that meet its marker's
52/// guarantees.
53pub struct Promises<O, R> {
54 _promised: PhantomData<fn() -> (O, R)>,
55}
56
57impl Promises<NoOrder, AtLeastOnce> {
58 /// Promises nothing: elements may arrive in any order, and any may be delivered more than
59 /// once.
60 pub fn new() -> Self {
61 Promises {
62 _promised: PhantomData,
63 }
64 }
65}
66
67impl Default for Promises<NoOrder, AtLeastOnce> {
68 fn default() -> Self {
69 Self::new()
70 }
71}
72
73impl<O, R> Promises<O, R> {
74 fn retag<O2, R2>(self) -> Promises<O2, R2> {
75 Promises {
76 _promised: PhantomData,
77 }
78 }
79}
80
81impl<R> Promises<NoOrder, R> {
82 /// Promises the order: for a channel, each sender's messages are delivered in the order it
83 /// sent them; for an input, the stream's order is the order to process; for a membership
84 /// stream, each member's events arrive in the order they happened.
85 pub fn total_order(self, _proof: ManualProof) -> Promises<TotalOrder, R> {
86 self.retag()
87 }
88}
89
90impl<O> Promises<O, AtLeastOnce> {
91 /// Promises that nothing is delivered twice: a channel never duplicates a message (for
92 /// example by resending it after a reconnect), and every element of an input is a distinct
93 /// event.
94 pub fn exactly_once(self, _proof: ManualProof) -> Promises<O, ExactlyOnce> {
95 self.retag()
96 }
97}
98
99/// A channel whose guarantees promises of ordering `O` and retries `R` meet: each guarantee the
100/// channel needs is weaker than (or the same as) the one promised. It is what [`EmbeddedInputChannel::new`]
101/// requires, and lets a reusable transport state once what it provides:
102/// `fn source<C: SatisfiedBy<TotalOrder, ExactlyOnce>>()` serves every channel that needs at
103/// most that.
104pub trait SatisfiedBy<O, R>: EmbeddedChannel {}
105
106impl<K, O, R> SatisfiedBy<O, R> for K
107where
108 K: EmbeddedChannel,
109 K::Ordering: OrderPromisedBy<O, K>,
110 K::Retries: RetriesPromisedBy<R, K>,
111{
112}
113
114/// A needed order (`Self`) that the promised order `Promised` meets, for the channel end `End`.
115/// Only order is promised, so a promise is needed only for `TotalOrder`.
116#[diagnostic::on_unimplemented(
117 message = "`{End}` relies on its elements arriving in order, but the host does not promise it",
118 label = "does not promise `TotalOrder`",
119 note = "add `.total_order(manual_proof!(/** why the order is kept */))` to its `Promises`"
120)]
121trait OrderPromisedBy<Promised, End> {}
122
123impl<Promised: Ordering, End> OrderPromisedBy<Promised, End> for NoOrder {}
124impl<End> OrderPromisedBy<TotalOrder, End> for TotalOrder {}
125
126/// A needed retry guarantee (`Self`) that the promised one `Promised` meets, for `End`.
127#[diagnostic::on_unimplemented(
128 message = "`{End}` relies on each element arriving exactly once, but the host does not promise it",
129 label = "does not promise `ExactlyOnce`",
130 note = "add `.exactly_once(manual_proof!(/** why nothing is delivered twice */))` to its `Promises`"
131)]
132trait RetriesPromisedBy<Promised, End> {}
133
134impl<Promised: Retries, End> RetriesPromisedBy<Promised, End> for AtLeastOnce {}
135impl<End> RetriesPromisedBy<ExactlyOnce, End> for ExactlyOnce {}
136
137/// A channel flowing into an embedded dataflow, as the host provides it: a `Stream` for a stream
138/// input or a membership stream, for the channel `C` (its generated marker type, see
139/// [`EmbeddedChannel`]). The receiving end of a network channel is a [`EmbeddedNetworkInputChannel`] instead.
140///
141/// An `EmbeddedInputChannel` exists only with promises that meet `C`'s guarantees:
142/// [`EmbeddedInputChannel::new`] takes the [`Promises`] and checks them, so wiring a channel always
143/// states its guarantees and a mismatch with the dataflow is a type error at that call. Promising
144/// more than a channel needs is fine. `C` is inferred from the field the `EmbeddedInputChannel` fills.
145pub struct EmbeddedInputChannel<C, S> {
146 inner: S,
147 _channel: PhantomData<fn() -> C>,
148}
149
150impl<C: EmbeddedChannel, S> EmbeddedInputChannel<C, S> {
151 /// Wraps a host stream for channel `C`, with `promises` that must meet its guarantees.
152 pub fn new<O, R>(inner: S, promises: Promises<O, R>) -> Self
153 where
154 C: SatisfiedBy<O, R>,
155 {
156 let _ = promises;
157 EmbeddedInputChannel {
158 inner,
159 _channel: PhantomData,
160 }
161 }
162}
163
164impl<C, S> EmbeddedInputChannel<C, S> {
165 /// The host's stream. Used by generated code.
166 #[doc(hidden)]
167 pub fn into_inner(self) -> S {
168 self.inner
169 }
170}
171
172/// The configuration of a network channel crossing the boundary of an embedded dataflow: the
173/// transport type `Transport` of the network config the dataflow sends with (for example
174/// `Tcp<FailStop>` for `TCP.fail_stop()`, or `EmbeddedTransport<ExactlyOnce, NoOrder, FailStop>`
175/// for `EMBEDDED.may_reorder().never_duplicates().never_drops()`), and its serialization `Serialization` (`Bincode`, or `Embedded`
176/// for an unserialized `EMBEDDED` channel).
177///
178/// The markers of both ends of a network channel implement it, alongside [`EmbeddedChannel`]
179/// for the stream at that end. The host acknowledges the configuration exactly at each end with
180/// a [`ChannelConfiguration`], so that changing the channel's network config fails the host's
181/// build at both ends until it acknowledges the change.
182pub trait EmbeddedNetworkChannel: EmbeddedChannel {
183 /// The transport type of the channel's network config.
184 type Transport;
185
186 /// The serialization of the channel's network config.
187 type Serialization;
188}
189
190/// The configuration of a network channel as the host acknowledges it: the transport `Tr` and
191/// serialization `S` of its network config, which must match the channel's exactly (see
192/// [`EmbeddedNetworkChannel`]). It is built from the same network config value the dataflow
193/// sends with; the config's name is ignored.
194pub struct ChannelConfiguration<Tr, S> {
195 _acknowledged: PhantomData<fn() -> (Tr, S)>,
196}
197
198impl<Tr, S> ChannelConfiguration<Tr, S> {
199 /// Acknowledges a channel configured with `config`, the same network config the dataflow
200 /// sends with (for example `TCP.fail_stop().bincode()`).
201 pub fn new<Name>(config: NetworkingConfig<Tr, S, Name>) -> Self {
202 let _ = config;
203 ChannelConfiguration {
204 _acknowledged: PhantomData,
205 }
206 }
207}
208
209/// A network channel end whose configuration is exactly the acknowledged transport `Tr` and
210/// serialization `S`. Each part is checked separately, so a mismatch names the part the host
211/// acknowledges wrongly. It is what [`EmbeddedNetworkOutputChannel::new`] and [`EmbeddedNetworkInputChannel::new`] require.
212pub trait ConfiguredAs<Tr, S>: EmbeddedNetworkChannel {}
213
214impl<C, Tr, S> ConfiguredAs<Tr, S> for C
215where
216 C: EmbeddedNetworkChannel,
217 Tr: AcknowledgesTransport<C::Transport, C>,
218 S: AcknowledgesSerialization<C::Serialization, C>,
219{
220}
221
222/// Checks the acknowledged transport separately, so a mismatch names it.
223#[diagnostic::on_unimplemented(
224 message = "the channel end `{End}` is configured with `{Declared}`, but the host acknowledges `{Self}`",
225 label = "acknowledges `{Self}`",
226 note = "acknowledge the same network config the dataflow sends with; the marker's docs give it"
227)]
228trait AcknowledgesTransport<Declared, End> {}
229
230impl<X, End> AcknowledgesTransport<X, End> for X {}
231
232/// Checks the acknowledged serialization separately, so a mismatch names it.
233#[diagnostic::on_unimplemented(
234 message = "the channel end `{End}` is serialized with `{Declared}`, but the host acknowledges `{Self}`",
235 label = "acknowledges `{Self}`",
236 note = "acknowledge the same network config the dataflow sends with; the marker's docs give it"
237)]
238trait AcknowledgesSerialization<Declared, End> {}
239
240impl<X, End> AcknowledgesSerialization<X, End> for X {}
241
242/// The sending end of a network channel, as the host provides it: an `FnMut` sink that hands
243/// each message to the host's transport, for the channel end `C` (its generated marker type,
244/// under `outgoing`).
245///
246/// The host agrees to both inputs of the dataflow's send: [`EmbeddedNetworkOutputChannel::new`] takes the
247/// [`ChannelConfiguration`] the host acknowledges, which must match the channel's exactly, and the
248/// [`Acknowledgements`] of the stream the dataflow sends, which must cover every way it may be
249/// weaker than ordered and exactly-once, as for an [`EmbeddedOutputChannel`]. `C` is inferred from the
250/// field the `EmbeddedNetworkOutputChannel` fills.
251pub struct EmbeddedNetworkOutputChannel<C, S> {
252 inner: S,
253 _channel: PhantomData<fn() -> C>,
254}
255
256impl<C: EmbeddedNetworkChannel, S> EmbeddedNetworkOutputChannel<C, S> {
257 /// Wraps a host sink for the channel end `C`, with the acknowledgements of its
258 /// configuration (`channel`) and of the stream sent on it (`acknowledgements`).
259 pub fn new<Tr, Ser, O, R>(
260 inner: S,
261 channel: ChannelConfiguration<Tr, Ser>,
262 acknowledgements: Acknowledgements<O, R>,
263 ) -> Self
264 where
265 C: ConfiguredAs<Tr, Ser> + AcknowledgedBy<O, R>,
266 {
267 let _ = (channel, acknowledgements);
268 EmbeddedNetworkOutputChannel {
269 inner,
270 _channel: PhantomData,
271 }
272 }
273}
274
275impl<C, S> EmbeddedNetworkOutputChannel<C, S> {
276 /// The host's sink. Used by generated code.
277 #[doc(hidden)]
278 pub fn into_inner(self) -> S {
279 self.inner
280 }
281}
282
283/// The receiving end of a network channel, as the host provides it: a `Stream` of the messages
284/// its transport delivers, for the channel end `C` (its generated marker type, under
285/// `incoming`).
286///
287/// The host agrees to the channel's configuration and to the stream it delivers:
288/// [`EmbeddedNetworkInputChannel::new`] takes the [`ChannelConfiguration`] the host acknowledges,
289/// which must match exactly, and the [`Promises`] of the stream it delivers, which must meet what
290/// the dataflow relies on, as for an [`EmbeddedInputChannel`]. That stream is the sent stream weakened by
291/// the channel's configuration (a channel that may reorder delivers a `NoOrder` stream), so it
292/// may be weaker than what the sending end acknowledges. `C` is inferred from the field the
293/// `EmbeddedNetworkInputChannel` fills.
294pub struct EmbeddedNetworkInputChannel<C, S> {
295 inner: S,
296 _channel: PhantomData<fn() -> C>,
297}
298
299impl<C: EmbeddedNetworkChannel, S> EmbeddedNetworkInputChannel<C, S> {
300 /// Wraps a host stream for the channel end `C`, with the acknowledgement of its
301 /// configuration (`channel`) and the promises of the stream it delivers (`promises`).
302 pub fn new<Tr, Ser, O, R>(
303 inner: S,
304 channel: ChannelConfiguration<Tr, Ser>,
305 promises: Promises<O, R>,
306 ) -> Self
307 where
308 C: ConfiguredAs<Tr, Ser> + SatisfiedBy<O, R>,
309 {
310 let _ = (channel, promises);
311 EmbeddedNetworkInputChannel {
312 inner,
313 _channel: PhantomData,
314 }
315 }
316}
317
318impl<C, S> EmbeddedNetworkInputChannel<C, S> {
319 /// The host's stream. Used by generated code.
320 #[doc(hidden)]
321 pub fn into_inner(self) -> S {
322 self.inner
323 }
324}
325
326/// What a host acknowledges about a stream the dataflow provides (an output, or the stream sent
327/// on a network channel): each way it may be weaker than ordered and exactly-once. The stream's
328/// type already says what it may do, so an acknowledgement needs no justification: calling it is
329/// the host's agreement, and a stream that becomes weaker fails the host's build until the host
330/// acknowledges the change. An
331/// [`EmbeddedOutputChannel`] or [`EmbeddedNetworkOutputChannel`] is built from a sink and the `Acknowledgements` that
332/// cover what its marker's stream may do.
333pub struct Acknowledgements<O, R> {
334 _acknowledged: PhantomData<fn() -> (O, R)>,
335}
336
337impl Acknowledgements<TotalOrder, ExactlyOnce> {
338 /// Acknowledges nothing: the sink relies on receiving elements in the dataflow's order, each
339 /// exactly once.
340 pub fn new() -> Self {
341 Acknowledgements {
342 _acknowledged: PhantomData,
343 }
344 }
345}
346
347impl Default for Acknowledgements<TotalOrder, ExactlyOnce> {
348 fn default() -> Self {
349 Self::new()
350 }
351}
352
353impl<O, R> Acknowledgements<O, R> {
354 fn retag<O2, R2>(self) -> Acknowledgements<O2, R2> {
355 Acknowledgements {
356 _acknowledged: PhantomData,
357 }
358 }
359}
360
361impl<R> Acknowledgements<TotalOrder, R> {
362 /// Acknowledges that elements may arrive in any order.
363 pub fn no_order(self) -> Acknowledgements<NoOrder, R> {
364 self.retag()
365 }
366}
367
368impl<O> Acknowledgements<O, ExactlyOnce> {
369 /// Acknowledges that an element may arrive more than once.
370 pub fn at_least_once(self) -> Acknowledgements<O, AtLeastOnce> {
371 self.retag()
372 }
373}
374
375/// An output whose guarantees acknowledgements of ordering `O` and retries `R` cover: each
376/// acknowledged guarantee is weaker than (or the same as) the one the output provides. It is
377/// what [`EmbeddedOutputChannel::new`]
378/// requires.
379pub trait AcknowledgedBy<O, R>: EmbeddedChannel {}
380
381impl<K, O, R> AcknowledgedBy<O, R> for K
382where
383 K: EmbeddedChannel,
384 K::Ordering: OrderAcknowledgedBy<O, K>,
385 K::Retries: RetriesAcknowledgedBy<R, K>,
386{
387}
388
389/// A provided order (`Self`) that the acknowledged order `Acknowledged` covers, for `End`.
390/// Only a weaker order is acknowledged, so an acknowledgement is needed only for `NoOrder`.
391#[diagnostic::on_unimplemented(
392 message = "`{End}` may deliver its elements in any order, which the host must acknowledge",
393 label = "does not acknowledge `NoOrder`",
394 note = "add `.no_order()` to its `Acknowledgements`"
395)]
396trait OrderAcknowledgedBy<Acknowledged, End> {}
397
398impl<Acknowledged: Ordering, End> OrderAcknowledgedBy<Acknowledged, End> for TotalOrder {}
399impl<End> OrderAcknowledgedBy<NoOrder, End> for NoOrder {}
400
401/// A provided retry guarantee (`Self`) that the acknowledged one `Acknowledged` covers, for `End`.
402#[diagnostic::on_unimplemented(
403 message = "`{End}` may deliver an element more than once, which the host must acknowledge",
404 label = "does not acknowledge `AtLeastOnce`",
405 note = "add `.at_least_once()` to its `Acknowledgements`"
406)]
407trait RetriesAcknowledgedBy<Acknowledged, End> {}
408
409impl<Acknowledged: Retries, End> RetriesAcknowledgedBy<Acknowledged, End> for ExactlyOnce {}
410impl<End> RetriesAcknowledgedBy<AtLeastOnce, End> for AtLeastOnce {}
411
412/// An output of an embedded dataflow, as the host provides it: an `FnMut` sink for the channel
413/// `M` (its generated marker type, under `outputs`), whose [`EmbeddedChannel`] guarantees are
414/// what the dataflow's output stream provides.
415///
416/// An `EmbeddedOutputChannel` exists only with acknowledgements that cover what the output may do:
417/// [`EmbeddedOutputChannel::new`] takes the [`Acknowledgements`] and checks them, so a sink cannot
418/// silently receive elements out of order or more than once. Acknowledging more than an output
419/// may do is fine. `M` is inferred from the field the `EmbeddedOutputChannel` fills.
420pub struct EmbeddedOutputChannel<M, S> {
421 inner: S,
422 _output: PhantomData<fn() -> M>,
423}
424
425impl<M: EmbeddedChannel, S> EmbeddedOutputChannel<M, S> {
426 /// Wraps a host sink for output `M`, with `acknowledgements` that cover what `M` may do.
427 pub fn new<O, R>(inner: S, acknowledgements: Acknowledgements<O, R>) -> Self
428 where
429 M: AcknowledgedBy<O, R>,
430 {
431 let _ = acknowledgements;
432 EmbeddedOutputChannel {
433 inner,
434 _output: PhantomData,
435 }
436 }
437}
438
439impl<M, S> EmbeddedOutputChannel<M, S> {
440 /// The host's sink. Used by generated code.
441 #[doc(hidden)]
442 pub fn into_inner(self) -> S {
443 self.inner
444 }
445}