Skip to main content

hydro_lang/networking/
mod.rs

1//! Types for configuring network channels with serialization formats, transports, etc.
2
3use std::marker::PhantomData;
4
5use serde::Serialize;
6use serde::de::DeserializeOwned;
7
8use crate::live_collections::stream::networking::{deserialize_bincode, serialize_bincode};
9
10#[sealed::sealed]
11trait SerKind<T: ?Sized> {
12    fn serialize_thunk(is_demux: bool) -> syn::Expr;
13
14    fn deserialize_thunk(tagged: Option<&syn::Type>) -> syn::Expr;
15}
16
17/// Serialize items using the [`bincode`] crate.
18pub enum Bincode {}
19
20#[sealed::sealed]
21impl<T: Serialize + DeserializeOwned> SerKind<T> for Bincode {
22    fn serialize_thunk(is_demux: bool) -> syn::Expr {
23        serialize_bincode::<T>(is_demux)
24    }
25
26    fn deserialize_thunk(tagged: Option<&syn::Type>) -> syn::Expr {
27        deserialize_bincode::<T>(tagged)
28    }
29}
30
31/// An unconfigured serialization backend.
32pub enum NoSer {}
33
34#[sealed::sealed]
35trait TransportKind {}
36
37#[sealed::sealed]
38#[diagnostic::on_unimplemented(
39    message = "TCP transport requires a failure policy. For example, `TCP.fail_stop()` stops sending messages after a failed connection."
40)]
41trait TcpFailPolicy {}
42
43/// A TCP failure policy that stops sending messages after a failed connection.
44pub enum FailStop {}
45#[sealed::sealed]
46impl TcpFailPolicy for FailStop {}
47
48/// Send items across a length-delimited TCP channel.
49pub struct Tcp<F> {
50    _phantom: PhantomData<F>,
51}
52
53#[sealed::sealed]
54impl<F: TcpFailPolicy> TransportKind for Tcp<F> {}
55
56/// A networking backend implementation that supports items of type `T`.
57#[sealed::sealed]
58pub trait NetworkFor<T: ?Sized> {
59    /// Generates serialization logic for sending `T`.
60    fn serialize_thunk(is_demux: bool) -> syn::Expr;
61
62    /// Generates deserialization logic for receiving `T`.
63    fn deserialize_thunk(tagged: Option<&syn::Type>) -> syn::Expr;
64
65    /// Returns the optional name of the network channel.
66    fn name(&self) -> Option<&str>;
67}
68
69/// A network channel configuration with `T` as transport backend and `S` as the serialization
70/// backend.
71pub struct NetworkingConfig<Tr: ?Sized, S: ?Sized, Name = ()> {
72    name: Option<Name>,
73    _phantom: (PhantomData<Tr>, PhantomData<S>),
74}
75
76impl<Tr: ?Sized, S: ?Sized> NetworkingConfig<Tr, S> {
77    /// Configures the network channel to use [`bincode`] to serialize items.
78    pub const fn bincode(self) -> NetworkingConfig<Tr, Bincode> {
79        NetworkingConfig {
80            name: self.name,
81            _phantom: (PhantomData, PhantomData),
82        }
83    }
84
85    /// Names the network channel and enables stable communication across multiple service versions.
86    pub fn name(self, name: impl Into<String>) -> NetworkingConfig<Tr, S, String> {
87        NetworkingConfig {
88            name: Some(name.into()),
89            _phantom: (PhantomData, PhantomData),
90        }
91    }
92}
93
94impl<S: ?Sized> NetworkingConfig<Tcp<()>, S> {
95    /// Configures the TCP transport to stop sending messages after a failed connection.
96    ///
97    /// Note that the Hydro simulator will not simulate connection failures that impact the
98    /// *liveness* of a program. If an output assertion depends on a `fail_stop` channel
99    /// making progress, that channel will not experience a failure that would cause the test to
100    /// block indefinitely. However, any *safety* issues caused by connection failures will still
101    /// be caught, such as a race condition between a failed connection and some other message.
102    pub const fn fail_stop(self) -> NetworkingConfig<Tcp<FailStop>, S> {
103        NetworkingConfig {
104            name: self.name,
105            _phantom: (PhantomData, PhantomData),
106        }
107    }
108}
109
110#[sealed::sealed]
111impl<Tr: ?Sized, S: ?Sized, T: ?Sized> NetworkFor<T> for NetworkingConfig<Tr, S>
112where
113    Tr: TransportKind,
114    S: SerKind<T>,
115{
116    fn serialize_thunk(is_demux: bool) -> syn::Expr {
117        S::serialize_thunk(is_demux)
118    }
119
120    fn deserialize_thunk(tagged: Option<&syn::Type>) -> syn::Expr {
121        S::deserialize_thunk(tagged)
122    }
123
124    fn name(&self) -> Option<&str> {
125        None
126    }
127}
128
129#[sealed::sealed]
130impl<Tr: ?Sized, S: ?Sized, T: ?Sized> NetworkFor<T> for NetworkingConfig<Tr, S, String>
131where
132    Tr: TransportKind,
133    S: SerKind<T>,
134{
135    fn serialize_thunk(is_demux: bool) -> syn::Expr {
136        S::serialize_thunk(is_demux)
137    }
138
139    fn deserialize_thunk(tagged: Option<&syn::Type>) -> syn::Expr {
140        S::deserialize_thunk(tagged)
141    }
142
143    fn name(&self) -> Option<&str> {
144        self.name.as_deref()
145    }
146}
147
148/// A network channel that uses length-delimited TCP for transport.
149pub const TCP: NetworkingConfig<Tcp<()>, NoSer> = NetworkingConfig {
150    name: None,
151    _phantom: (PhantomData, PhantomData),
152};