Skip to main content

hydro_lang/
lib.rs

1#![cfg_attr(docsrs, feature(doc_cfg))]
2#![cfg_attr(not(stageleft_trybuild), warn(missing_docs))]
3
4//! Hydro is a high-level distributed programming framework for Rust.
5//! Hydro can help you quickly write scalable distributed services that are correct by construction.
6//! Much like Rust helps with memory safety, Hydro helps with [distributed safety](https://hydro.run/docs/hydro/reference/correctness/).
7//!
8//! The core Hydro API involves [live collections](https://hydro.run/docs/hydro/reference/introduction/live-collections), which represent asynchronously
9//! updated sources of data such as incoming network requests and application state. The most common live collection is
10//! [`live_collections::stream::Stream`]; other live collections can be found in [`live_collections`].
11//!
12//! Hydro uses a unique compilation approach where you define deployment logic as Rust code alongside your distributed system implementation.
13//! For more details on this API, see the [Hydro docs](https://hydro.run/docs/hydro/reference/deploy/) and the [`deploy`] module.
14
15stageleft::stageleft_no_entry_crate!();
16
17#[cfg(feature = "runtime_support")]
18#[cfg_attr(docsrs, doc(cfg(feature = "runtime_support")))]
19#[doc(hidden)]
20pub mod runtime_support {
21    pub use ::{bincode, dfir_rs, slotmap, stageleft};
22    #[cfg(feature = "sim")]
23    pub use colored;
24    #[cfg(feature = "deploy_integration")]
25    pub use hydro_deploy_integration;
26    #[cfg(feature = "tokio")]
27    pub use tokio;
28
29    #[cfg(feature = "deploy_integration")]
30    pub mod launch;
31}
32
33#[doc(hidden)]
34pub mod macro_support {
35    pub use copy_span;
36    #[cfg(feature = "trybuild")]
37    pub use ctor;
38}
39
40pub mod prelude {
41    // taken from `tokio`
42    //! A "prelude" for users of the `hydro_lang` crate.
43    //!
44    //! This prelude is similar to the standard library's prelude in that you'll almost always want to import its entire contents, but unlike the standard library's prelude you'll have to do so manually:
45    //! ```
46    //! # #![allow(warnings)]
47    //! use hydro_lang::prelude::*;
48    //! ```
49    //!
50    //! The prelude may grow over time as additional items see ubiquitous use.
51
52    pub use stageleft::q;
53
54    pub use crate::compile::builder::FlowBuilder;
55    pub use crate::live_collections::boundedness::{Bounded, Unbounded};
56    pub use crate::live_collections::keyed_singleton::{KeyedSingleton, MonotonicKeys};
57    pub use crate::live_collections::keyed_stream::KeyedStream;
58    pub use crate::live_collections::optional::{InitNone, Optional};
59    pub use crate::live_collections::singleton::Singleton;
60    pub use crate::live_collections::sliced::sliced;
61    pub use crate::live_collections::stream::Stream;
62    pub use crate::location::{Cluster, External, Location as _, Process, Tick};
63    pub use crate::networking::{TCP, UDP};
64    pub use crate::nondet::{NonDet, nondet};
65    pub use crate::properties::{ConsistencyProof, ManualProof, manual_proof};
66
67    #[cfg(feature = "trybuild")]
68    /// A macro to set up a Hydro crate.
69    #[macro_export]
70    macro_rules! setup {
71        () => {
72            stageleft::stageleft_no_entry_crate!();
73
74            #[cfg(test)]
75            mod test_init {
76                $crate::macro_support::ctor::declarative::ctor!(
77                    #[ctor(unsafe)]
78                    fn init() {
79                        $crate::compile::init_test();
80                    }
81                );
82            }
83        };
84    }
85
86    #[cfg(not(feature = "trybuild"))]
87    /// A macro to set up a Hydro crate.
88    #[macro_export]
89    macro_rules! setup {
90        () => {
91            stageleft::stageleft_no_entry_crate!();
92        };
93    }
94}
95
96#[cfg(feature = "dfir_context")]
97#[cfg_attr(docsrs, doc(cfg(feature = "dfir_context")))]
98pub mod runtime_context;
99
100pub mod nondet;
101
102pub mod live_collections;
103
104pub mod location;
105
106pub mod networking;
107
108pub mod properties;
109
110pub mod telemetry;
111
112#[cfg(any(
113    feature = "deploy",
114    feature = "sim",
115    feature = "deploy_integration" // hidden internal feature enabled in the trybuild
116))]
117#[cfg_attr(docsrs, doc(cfg(any(feature = "deploy", feature = "sim"))))]
118pub mod deploy;
119
120#[cfg(feature = "sim")]
121#[cfg_attr(docsrs, doc(cfg(feature = "sim")))]
122pub mod sim;
123
124pub mod sim_hooks;
125
126pub mod forward_handle;
127
128pub mod compile;
129
130pub mod handoff_ref;
131
132mod manual_expr;
133
134#[cfg(stageleft_runtime)]
135#[cfg(feature = "viz")]
136#[cfg_attr(docsrs, doc(cfg(feature = "viz")))]
137#[expect(missing_docs, reason = "TODO")]
138pub mod viz;
139
140#[cfg_attr(
141    feature = "stageleft_macro_entrypoint",
142    expect(missing_docs, reason = "staging internals")
143)]
144mod staging_util;
145
146#[cfg(feature = "deploy")]
147#[cfg_attr(docsrs, doc(cfg(feature = "deploy")))]
148pub mod test_util;
149
150#[cfg(feature = "build")]
151ctor::declarative::ctor!(
152    #[ctor(unsafe)]
153    fn init_rewrites() {
154        stageleft::add_private_reexport(
155            vec!["tokio_util", "codec", "lines_codec"],
156            vec!["tokio_util", "codec"],
157        );
158        // TODO: remove once stabilized
159        stageleft::add_private_reexport(
160            vec!["core", "io", "error", "Error"],
161            vec!["std", "io", "Error"],
162        );
163    }
164);
165
166#[cfg(all(test, feature = "trybuild"))]
167mod test_init {
168    ctor::declarative::ctor!(
169        #[ctor(unsafe)]
170        fn init() {
171            crate::compile::init_test();
172            // Install a tracing subscriber so diagnostics (e.g. the `hydro_build` build-timing
173            // events used by scripts/bench_trybuild.sh) can be enabled via RUST_LOG.
174            crate::telemetry::initialize_tracing();
175        }
176    );
177}
178
179/// Creates a newtype wrapper around an integer type.
180///
181/// Usage:
182/// ```rust,ignore
183/// hydro_lang::newtype_counter! {
184///     /// My counter.
185///     pub struct MyCounter(u32);
186///
187///     /// My secret counter.
188///     struct SecretCounter(u64);
189/// }
190/// ```
191#[doc(hidden)]
192#[macro_export]
193macro_rules! newtype_counter {
194    (
195        $(
196            $( #[$attr:meta] )*
197            $vis:vis struct $name:ident($typ:ty);
198        )*
199    ) => {
200        $(
201            $( #[$attr] )*
202            #[repr(transparent)]
203            #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
204            $vis struct $name($typ);
205
206            #[allow(clippy::allow_attributes, dead_code, reason = "macro-generated methods may be unused")]
207            impl $name {
208                /// Reveals the inner ID.
209                pub fn into_inner(self) -> $typ {
210                    self.0
211                }
212            }
213
214            impl std::fmt::Display for $name {
215                fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
216                    write!(f, "{}", self.0)
217                }
218            }
219
220            impl serde::ser::Serialize for $name {
221                fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
222                where
223                    S: serde::Serializer
224                {
225                    serde::ser::Serialize::serialize(&self.0, serializer)
226                }
227            }
228
229            impl<'de> serde::de::Deserialize<'de> for $name {
230                fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
231                where
232                    D: serde::Deserializer<'de>
233                {
234                    serde::de::Deserialize::deserialize(deserializer).map(Self)
235                }
236            }
237
238            #[sealed::sealed]
239            impl $crate::Countable for $name {
240                fn from_count(val: usize) -> Self {
241                    Self(val as $typ)
242                }
243            }
244        )*
245    };
246}
247
248/// Sealed trait implemented by ID types produced via [`newtype_counter!`].
249///
250/// This allows [`Counter<T>`] to mint new IDs without exposing a public
251/// constructor on the ID types themselves.
252#[doc(hidden)]
253#[sealed::sealed]
254pub trait Countable {
255    #[doc(hidden)]
256    fn from_count(val: usize) -> Self;
257}
258
259/// An opaque counter that produces unique IDs of type `T` via [`Counter::get_and_increment`].
260///
261/// This is separate from the ID types themselves so that holding an ID does not
262/// give the ability to mint new IDs.
263#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
264pub struct Counter<T: Countable>(usize, std::marker::PhantomData<T>);
265
266impl<T: Countable> Default for Counter<T> {
267    fn default() -> Self {
268        Self(0, std::marker::PhantomData)
269    }
270}
271
272impl<T: Countable> Counter<T> {
273    /// Gets the current counter value and increments for the next call.
274    pub fn get_and_increment(&mut self) -> T {
275        let id = self.0;
276        self.0 += 1;
277        T::from_count(id)
278    }
279
280    /// Returns an iterator from zero up to (but excluding) the current counter value.
281    ///
282    /// This is useful for iterating already-allocated values.
283    pub fn range_up_to(&self) -> impl DoubleEndedIterator<Item = T> + std::iter::FusedIterator {
284        (0..self.0).map(T::from_count)
285    }
286}