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::{EMBEDDED, TCP, UDP};
64    pub use crate::nondet::{NonDet, nondet};
65    pub use crate::properties::{
66        ConsistencyProof, ManualProof, VerusCommutativeProof, manual_proof, verus_panic,
67        verus_proof_commutative_effect, verus_proof_commutative_filter,
68        verus_proof_commutative_fold, verus_proof_commutative_map,
69    };
70
71    #[cfg(feature = "trybuild")]
72    /// A macro to set up a Hydro crate.
73    #[macro_export]
74    macro_rules! setup {
75        () => {
76            stageleft::stageleft_no_entry_crate!();
77
78            #[cfg(test)]
79            mod test_init {
80                $crate::macro_support::ctor::declarative::ctor!(
81                    #[ctor(unsafe)]
82                    fn init() {
83                        $crate::compile::init_test();
84                    }
85                );
86            }
87        };
88    }
89
90    #[cfg(not(feature = "trybuild"))]
91    /// A macro to set up a Hydro crate.
92    #[macro_export]
93    macro_rules! setup {
94        () => {
95            stageleft::stageleft_no_entry_crate!();
96        };
97    }
98}
99
100#[cfg(feature = "dfir_context")]
101#[cfg_attr(docsrs, doc(cfg(feature = "dfir_context")))]
102pub mod runtime_context;
103
104pub mod nondet;
105
106pub mod live_collections;
107
108pub mod location;
109
110pub mod networking;
111
112pub mod properties;
113
114pub mod telemetry;
115
116#[cfg(any(
117    feature = "deploy",
118    feature = "sim",
119    feature = "deploy_integration" // hidden internal feature enabled in the trybuild
120))]
121#[cfg_attr(docsrs, doc(cfg(any(feature = "deploy", feature = "sim"))))]
122pub mod deploy;
123
124#[cfg(feature = "sim")]
125#[cfg_attr(docsrs, doc(cfg(feature = "sim")))]
126pub mod sim;
127
128pub mod sim_hooks;
129
130pub mod forward_handle;
131
132pub mod compile;
133
134pub mod handoff_ref;
135
136mod manual_expr;
137
138#[cfg(stageleft_runtime)]
139#[cfg(feature = "viz")]
140#[cfg_attr(docsrs, doc(cfg(feature = "viz")))]
141#[expect(missing_docs, reason = "TODO")]
142pub mod viz;
143
144#[cfg_attr(
145    feature = "stageleft_macro_entrypoint",
146    expect(missing_docs, reason = "staging internals")
147)]
148mod staging_util;
149
150#[cfg(feature = "deploy")]
151#[cfg_attr(docsrs, doc(cfg(feature = "deploy")))]
152pub mod test_util;
153
154#[cfg(feature = "build")]
155ctor::declarative::ctor!(
156    #[ctor(unsafe)]
157    fn init_rewrites() {
158        stageleft::add_private_reexport(
159            vec!["tokio_util", "codec", "lines_codec"],
160            vec!["tokio_util", "codec"],
161        );
162        // TODO: remove once stabilized
163        stageleft::add_private_reexport(
164            vec!["core", "io", "error", "Error"],
165            vec!["std", "io", "Error"],
166        );
167        // Nightly moved `Rc`/`Arc` (and their `Weak`s) into a private `alloc::rcs` module, so
168        // `type_name` reports `alloc::rcs::rc::Rc` / `alloc::rcs::arc::Arc`. Stageleft's
169        // built-in `alloc -> std` rule runs first and recurses, so match the rewritten form.
170        // TODO: remove once stageleft ships these rules (or the paths stabilize).
171        #[cfg(nightly)]
172        {
173            stageleft::add_private_reexport(vec!["std", "rcs", "rc"], vec!["std", "rc"]);
174            stageleft::add_private_reexport(vec!["std", "rcs", "arc"], vec!["std", "sync"]);
175        }
176    }
177);
178
179#[cfg(all(test, feature = "trybuild"))]
180mod test_init {
181    ctor::declarative::ctor!(
182        #[ctor(unsafe)]
183        fn init() {
184            crate::compile::init_test();
185            // Install a tracing subscriber so diagnostics (e.g. the `hydro_build` build-timing
186            // events used by scripts/bench_trybuild.sh) can be enabled via RUST_LOG.
187            crate::telemetry::initialize_tracing();
188        }
189    );
190}
191
192/// Creates a newtype wrapper around an integer type.
193///
194/// Usage:
195/// ```rust,ignore
196/// hydro_lang::newtype_counter! {
197///     /// My counter.
198///     pub struct MyCounter(u32);
199///
200///     /// My secret counter.
201///     struct SecretCounter(u64);
202/// }
203/// ```
204#[doc(hidden)]
205#[macro_export]
206macro_rules! newtype_counter {
207    (
208        $(
209            $( #[$attr:meta] )*
210            $vis:vis struct $name:ident($typ:ty);
211        )*
212    ) => {
213        $(
214            $( #[$attr] )*
215            #[repr(transparent)]
216            #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
217            $vis struct $name($typ);
218
219            #[allow(clippy::allow_attributes, dead_code, reason = "macro-generated methods may be unused")]
220            impl $name {
221                /// Reveals the inner ID.
222                pub fn into_inner(self) -> $typ {
223                    self.0
224                }
225            }
226
227            impl std::fmt::Display for $name {
228                fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
229                    write!(f, "{}", self.0)
230                }
231            }
232
233            impl serde::ser::Serialize for $name {
234                fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
235                where
236                    S: serde::Serializer
237                {
238                    serde::ser::Serialize::serialize(&self.0, serializer)
239                }
240            }
241
242            impl<'de> serde::de::Deserialize<'de> for $name {
243                fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
244                where
245                    D: serde::Deserializer<'de>
246                {
247                    serde::de::Deserialize::deserialize(deserializer).map(Self)
248                }
249            }
250
251            #[sealed::sealed]
252            impl $crate::Countable for $name {
253                fn from_count(val: usize) -> Self {
254                    Self(val as $typ)
255                }
256            }
257        )*
258    };
259}
260
261/// Sealed trait implemented by ID types produced via [`newtype_counter!`].
262///
263/// This allows [`Counter<T>`] to mint new IDs without exposing a public
264/// constructor on the ID types themselves.
265#[doc(hidden)]
266#[sealed::sealed]
267pub trait Countable {
268    #[doc(hidden)]
269    fn from_count(val: usize) -> Self;
270}
271
272/// An opaque counter that produces unique IDs of type `T` via [`Counter::get_and_increment`].
273///
274/// This is separate from the ID types themselves so that holding an ID does not
275/// give the ability to mint new IDs.
276#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
277pub struct Counter<T: Countable>(usize, std::marker::PhantomData<T>);
278
279impl<T: Countable> Default for Counter<T> {
280    fn default() -> Self {
281        Self(0, std::marker::PhantomData)
282    }
283}
284
285impl<T: Countable> Counter<T> {
286    /// Gets the current counter value and increments for the next call.
287    pub fn get_and_increment(&mut self) -> T {
288        let id = self.0;
289        self.0 += 1;
290        T::from_count(id)
291    }
292
293    /// Returns an iterator from zero up to (but excluding) the current counter value.
294    ///
295    /// This is useful for iterating already-allocated values.
296    pub fn range_up_to(&self) -> impl DoubleEndedIterator<Item = T> + std::iter::FusedIterator {
297        (0..self.0).map(T::from_count)
298    }
299}