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/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}
37
38pub mod prelude {
39    // taken from `tokio`
40    //! A "prelude" for users of the `hydro_lang` crate.
41    //!
42    //! 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:
43    //! ```
44    //! # #![allow(warnings)]
45    //! use hydro_lang::prelude::*;
46    //! ```
47    //!
48    //! The prelude may grow over time as additional items see ubiquitous use.
49
50    pub use stageleft::q;
51
52    pub use crate::compile::builder::FlowBuilder;
53    pub use crate::live_collections::boundedness::{Bounded, Unbounded};
54    pub use crate::live_collections::keyed_singleton::{KeyedSingleton, MonotonicKeys};
55    pub use crate::live_collections::keyed_stream::KeyedStream;
56    pub use crate::live_collections::optional::Optional;
57    pub use crate::live_collections::singleton::Singleton;
58    pub use crate::live_collections::sliced::sliced;
59    pub use crate::live_collections::stream::Stream;
60    pub use crate::location::{Cluster, External, Location as _, Process, Tick};
61    pub use crate::networking::TCP;
62    pub use crate::nondet::{NonDet, nondet};
63    pub use crate::properties::{ConsistencyProof, ManualProof, manual_proof};
64
65    /// A macro to set up a Hydro crate.
66    #[macro_export]
67    macro_rules! setup {
68        () => {
69            stageleft::stageleft_no_entry_crate!();
70
71            #[cfg(test)]
72            mod test_init {
73                #[ctor::ctor]
74                fn init() {
75                    $crate::compile::init_test();
76                }
77            }
78        };
79    }
80}
81
82#[cfg(feature = "dfir_context")]
83#[cfg_attr(docsrs, doc(cfg(feature = "dfir_context")))]
84pub mod runtime_context;
85
86pub mod nondet;
87
88pub mod live_collections;
89
90pub mod location;
91
92pub mod networking;
93
94pub mod properties;
95
96pub mod telemetry;
97
98#[cfg(any(
99    feature = "deploy",
100    feature = "sim",
101    feature = "deploy_integration" // hidden internal feature enabled in the trybuild
102))]
103#[cfg_attr(docsrs, doc(cfg(any(feature = "deploy", feature = "sim"))))]
104pub mod deploy;
105
106#[cfg(feature = "sim")]
107#[cfg_attr(docsrs, doc(cfg(feature = "sim")))]
108pub mod sim;
109
110pub mod forward_handle;
111
112pub mod compile;
113
114#[cfg(feature = "embedded_runtime")]
115#[cfg_attr(docsrs, doc(cfg(feature = "embedded_runtime")))]
116pub mod embedded;
117
118pub mod handoff_ref;
119
120mod manual_expr;
121
122#[cfg(stageleft_runtime)]
123#[cfg(feature = "viz")]
124#[cfg_attr(docsrs, doc(cfg(feature = "viz")))]
125#[expect(missing_docs, reason = "TODO")]
126pub mod viz;
127
128#[cfg_attr(
129    feature = "stageleft_macro_entrypoint",
130    expect(missing_docs, reason = "staging internals")
131)]
132mod staging_util;
133
134#[cfg(feature = "deploy")]
135#[cfg_attr(docsrs, doc(cfg(feature = "deploy")))]
136pub mod test_util;
137
138#[cfg(feature = "build")]
139#[ctor::ctor]
140fn init_rewrites() {
141    stageleft::add_private_reexport(
142        vec!["tokio_util", "codec", "lines_codec"],
143        vec!["tokio_util", "codec"],
144    );
145    // TODO: remove once stabilized
146    stageleft::add_private_reexport(
147        vec!["core", "io", "error", "Error"],
148        vec!["std", "io", "Error"],
149    );
150}
151
152#[cfg(all(test, feature = "trybuild"))]
153mod test_init {
154    #[ctor::ctor]
155    fn init() {
156        crate::compile::init_test();
157    }
158}
159
160/// Creates a newtype wrapper around an integer type.
161///
162/// Usage:
163/// ```rust,ignore
164/// hydro_lang::newtype_counter! {
165///     /// My counter.
166///     pub struct MyCounter(u32);
167///
168///     /// My secret counter.
169///     struct SecretCounter(u64);
170/// }
171/// ```
172#[doc(hidden)]
173#[macro_export]
174macro_rules! newtype_counter {
175    (
176        $(
177            $( #[$attr:meta] )*
178            $vis:vis struct $name:ident($typ:ty);
179        )*
180    ) => {
181        $(
182            $( #[$attr] )*
183            #[repr(transparent)]
184            #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
185            $vis struct $name($typ);
186
187            #[allow(clippy::allow_attributes, dead_code, reason = "macro-generated methods may be unused")]
188            impl $name {
189                /// Reveals the inner ID.
190                pub fn into_inner(self) -> $typ {
191                    self.0
192                }
193            }
194
195            impl std::fmt::Display for $name {
196                fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
197                    write!(f, "{}", self.0)
198                }
199            }
200
201            impl serde::ser::Serialize for $name {
202                fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
203                where
204                    S: serde::Serializer
205                {
206                    serde::ser::Serialize::serialize(&self.0, serializer)
207                }
208            }
209
210            impl<'de> serde::de::Deserialize<'de> for $name {
211                fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
212                where
213                    D: serde::Deserializer<'de>
214                {
215                    serde::de::Deserialize::deserialize(deserializer).map(Self)
216                }
217            }
218
219            #[sealed::sealed]
220            impl $crate::Countable for $name {
221                fn from_count(val: usize) -> Self {
222                    Self(val as $typ)
223                }
224            }
225        )*
226    };
227}
228
229/// Sealed trait implemented by ID types produced via [`newtype_counter!`].
230///
231/// This allows [`Counter<T>`] to mint new IDs without exposing a public
232/// constructor on the ID types themselves.
233#[doc(hidden)]
234#[sealed::sealed]
235pub trait Countable {
236    #[doc(hidden)]
237    fn from_count(val: usize) -> Self;
238}
239
240/// An opaque counter that produces unique IDs of type `T` via [`Counter::get_and_increment`].
241///
242/// This is separate from the ID types themselves so that holding an ID does not
243/// give the ability to mint new IDs.
244#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
245pub struct Counter<T: Countable>(usize, std::marker::PhantomData<T>);
246
247impl<T: Countable> Default for Counter<T> {
248    fn default() -> Self {
249        Self(0, std::marker::PhantomData)
250    }
251}
252
253impl<T: Countable> Counter<T> {
254    /// Gets the current counter value and increments for the next call.
255    pub fn get_and_increment(&mut self) -> T {
256        let id = self.0;
257        self.0 += 1;
258        T::from_count(id)
259    }
260
261    /// Returns an iterator from zero up to (but excluding) the current counter value.
262    ///
263    /// This is useful for iterating already-allocated values.
264    pub fn range_up_to(&self) -> impl DoubleEndedIterator<Item = T> + std::iter::FusedIterator {
265        (0..self.0).map(T::from_count)
266    }
267}