waitup/macros.rs
1//! Convenience macros for creating targets and configurations.
2
3/// Create TCP targets from a compact syntax.
4///
5/// Returns a `Result<Vec<Target>, WaitForError>` that contains either all valid targets
6/// or the first error encountered.
7///
8/// # Examples
9///
10/// ```rust
11/// use waitup::tcp_targets;
12///
13/// let targets = tcp_targets![
14/// "localhost" => 8080,
15/// "database" => 5432,
16/// "cache" => 6379,
17/// ]?;
18/// assert_eq!(targets.len(), 3);
19/// # Ok::<(), waitup::WaitForError>(())
20/// ```
21#[macro_export]
22macro_rules! tcp_targets {
23 ($($host:expr => $port:expr),* $(,)?) => {
24 {
25 #[expect(clippy::vec_init_then_push, reason = "macro expansion requires incremental push pattern")]
26 let result = || -> $crate::Result<Vec<$crate::Target>> {
27 let mut targets = Vec::new();
28 $(
29 targets.push($crate::Target::tcp($host, $port)?);
30 )*
31 return Ok(targets)
32 };
33 result()
34 }
35 };
36}
37
38/// Create HTTP targets from a compact syntax.
39///
40/// Returns a `Result<Vec<Target>, WaitForError>` that contains either all valid targets
41/// or the first error encountered.
42///
43/// # Examples
44///
45/// ```rust
46/// use waitup::http_targets;
47///
48/// let targets = http_targets![
49/// "https://api.example.com/health" => 200,
50/// "http://localhost:8080/status" => 204,
51/// ]?;
52/// assert_eq!(targets.len(), 2);
53/// # Ok::<(), waitup::WaitForError>(())
54/// ```
55#[macro_export]
56macro_rules! http_targets {
57 ($($url:expr => $status:expr),* $(,)?) => {
58 {
59 #[expect(clippy::vec_init_then_push, reason = "macro expansion requires incremental push pattern")]
60 let result = || -> $crate::Result<Vec<$crate::Target>> {
61 let mut targets = Vec::new();
62 $(
63 targets.push($crate::Target::http_url($url, $status)?);
64 )*
65 return Ok(targets)
66 };
67 result()
68 }
69 };
70}
71
72/// Create a wait configuration with a compact syntax.
73///
74/// # Examples
75///
76/// ```rust
77/// use waitup::wait_config;
78/// use std::time::Duration;
79///
80/// let config = wait_config! {
81/// timeout: Duration::from_secs(30),
82/// interval: Duration::from_millis(500),
83/// wait_for_any: false,
84/// };
85/// ```
86#[macro_export]
87macro_rules! wait_config {
88 (
89 $(timeout: $timeout:expr,)?
90 $(interval: $interval:expr,)?
91 $(max_interval: $max_interval:expr,)?
92 $(connection_timeout: $connection_timeout:expr,)?
93 $(max_retries: $max_retries:expr,)?
94 $(wait_for_any: $wait_for_any:expr,)?
95 ) => {
96 {
97 let mut builder = $crate::WaitConfig::builder();
98 $(builder = builder.timeout($timeout);)?
99 $(builder = builder.interval($interval);)?
100 $(builder = builder.max_interval($max_interval);)?
101 $(builder = builder.connection_timeout($connection_timeout);)?
102 $(builder = builder.max_retries($max_retries);)?
103 $(builder = builder.wait_for_any($wait_for_any);)?
104 builder.build()
105 }
106 };
107}
108
109/// Create common port configurations.
110///
111/// # Examples
112///
113/// ```rust
114/// use waitup::common_ports;
115///
116/// let ports = common_ports![http, https, ssh, postgres];
117/// assert_eq!(ports.len(), 4);
118/// ```
119#[macro_export]
120macro_rules! common_ports {
121 ($($port_name:ident),* $(,)?) => {
122 vec![
123 $(
124 $crate::Port::$port_name()
125 ),*
126 ]
127 };
128}
129
130/// Check that all targets in a collection are ready within a timeout.
131///
132/// Returns a `Result<WaitResult, WaitForError>` instead of panicking.
133///
134/// # Examples
135///
136/// ```rust,no_run
137/// use waitup::{check_ready, Target, WaitConfig};
138/// use std::time::Duration;
139///
140/// # #[tokio::main]
141/// # async fn main() -> Result<(), waitup::WaitForError> {
142/// let targets = vec![
143/// Target::tcp("localhost", 8080)?,
144/// ];
145///
146/// // Create config first to avoid temporary value issues
147/// let config = WaitConfig::builder()
148/// .timeout(Duration::from_secs(30))
149/// .build();
150/// let result = waitup::wait_for_connection(&targets, &config).await?;
151/// println!("All targets ready in {:?}", result.elapsed);
152/// # Ok(())
153/// # }
154/// ```
155#[macro_export]
156macro_rules! check_ready {
157 ($targets:expr, timeout: $timeout:expr) => {
158 {
159 let config = $crate::WaitConfig::builder()
160 .timeout($timeout)
161 .build();
162 $crate::wait_for_connection(&$targets, &config)
163 }
164 };
165 ($targets:expr, $($config_field:ident: $config_value:expr),+ $(,)?) => {
166 {
167 let config = $crate::wait_config! {
168 $($config_field: $config_value,)+
169 };
170 $crate::wait_for_connection(&$targets, &config)
171 }
172 };
173}
174
175/// Assert that all targets in a collection are ready within a timeout (panics on failure).
176///
177/// **Note:** This macro panics on failure. Consider using `check_ready!` for non-test code.
178///
179/// # Examples
180///
181/// ```rust,no_run
182/// use waitup::{assert_ready, Target};
183/// use std::time::Duration;
184///
185/// # #[tokio::main]
186/// # async fn main() -> Result<(), waitup::WaitForError> {
187/// let targets = vec![
188/// Target::tcp("localhost", 8080)?,
189/// ];
190///
191/// assert_ready!(targets, timeout: Duration::from_secs(30));
192/// # Ok(())
193/// # }
194/// ```
195#[macro_export]
196macro_rules! assert_ready {
197 ($targets:expr, timeout: $timeout:expr) => {
198 {
199 let config = $crate::WaitConfig::builder()
200 .timeout($timeout)
201 .build();
202 $crate::wait_for_connection(&$targets, &config)
203 .await
204 .unwrap_or_else(|e| panic!("Targets should be ready, but failed with: {}", e))
205 }
206 };
207 ($targets:expr, $($config_field:ident: $config_value:expr),+ $(,)?) => {
208 {
209 let config = $crate::wait_config! {
210 $($config_field: $config_value,)+
211 };
212 $crate::wait_for_connection(&$targets, &config)
213 .await
214 .unwrap_or_else(|e| panic!("Targets should be ready, but failed with: {}", e))
215 }
216 };
217}