Skip to main content

antithesis_sdk/assert/
macros.rs

1#[cfg(feature = "full")]
2#[doc(hidden)]
3#[macro_export]
4macro_rules! function {
5    ($static:ident) => {
6        // Define a do-nothing function `'_f()'` within the context of
7        // the function invoking an assertion.  Then the ``type_name`` of
8        // this do-nothing will be something like:
9        //
10        //     bincrate::binmod::do_stuff::_f
11        //
12        // After trimming off the last three chars ``::_f`` what remains is
13        // the full path to the name of the function invoking the assertion
14        //
15        // The result will be stored as a lazily initialized statics in
16        // `$static`, so that it can be available at
17        // assertion catalog registration time.
18        use $crate::once_cell::sync::Lazy;
19        fn _f() {}
20        static $static: $crate::once_cell::sync::Lazy<&'static str> =
21            $crate::once_cell::sync::Lazy::new(|| {
22                fn type_name_of<T>(_: T) -> &'static str {
23                    ::std::any::type_name::<T>()
24                }
25                let name = type_name_of(_f);
26                &name[..name.len() - 4]
27            });
28    };
29}
30
31/// Common handling used by all the assertion-related macros
32#[cfg(feature = "full")]
33#[doc(hidden)]
34#[macro_export]
35macro_rules! assert_helper {
36    // The handling of this pattern-arm of assert_helper
37    // is wrapped in a block {} to avoid name collisions
38    (condition = $condition:expr, $message:expr, $(details = $details:expr)?, $assert_type:path, $display_type:literal, must_hit = $must_hit:literal) => {{
39        // `$message` must be const evaluable
40        const _: &str = $message;
41
42        // Force evaluation of expressions.
43        let condition = $condition;
44        let details = &$crate::serde_json::Value::Null;
45        $(let details = $details;)?
46
47        $crate::function!(FUN_NAME);
48
49        use $crate::assert::AssertionCatalogInfo;
50        #[$crate::linkme::distributed_slice($crate::assert::ANTITHESIS_CATALOG)]
51        #[linkme(crate = $crate::linkme)] // Refer to our re-exported linkme.
52        static ALWAYS_CATALOG_ITEM: AssertionCatalogInfo = AssertionCatalogInfo {
53            assert_type: $assert_type,
54            display_type: $display_type,
55            condition: false,
56            message: $message,
57            class: ::std::module_path!(),
58            function: &FUN_NAME, /* function: &Lazy<&str> */
59            file: ::std::file!(),
60            begin_line: ::std::line!(),
61            begin_column: ::std::column!(),
62            must_hit: $must_hit,
63            id: $message,
64        };
65
66        let ptr_function = Lazy::force(&FUN_NAME);
67
68        static TRACKER: $crate::assert::TrackingInfo = $crate::assert::TrackingInfo::new();
69
70        $crate::assert::assert_impl(
71            $assert_type,                     /* assert_type */
72            $display_type,                    /* display_type */
73            condition,                        /* condition */
74            $message,                         /* message */
75            ::std::module_path!(),            /* class */
76            *ptr_function,                    /* function */
77            ::std::file!(),                   /* file */
78            ::std::line!(),                   /* line */
79            ::std::column!(),                 /* column */
80            true,                             /* hit */
81            $must_hit,                        /* must-hit */
82            $message,                         /* id */
83            details,                          /* details */
84            Some(&TRACKER),                   /* tracker */
85        )
86    }}; // end pattern-arm block
87}
88
89#[cfg(not(feature = "full"))]
90#[doc(hidden)]
91#[macro_export]
92macro_rules! assert_helper {
93    (condition = $condition:expr, $message:expr, $(details = $details:expr)?, $assert_type:path, $display_type:literal, must_hit = $must_hit:literal) => {{
94        // `$message` must be const evaluable
95        const _: &str = $message;
96
97        // Force evaluation of expressions, ensuring that
98        // any side effects of these expressions will always be
99        // evaluated at runtime - even if the assertion itself
100        // is supressed by the `no-antithesis-sdk` feature
101        let condition = $condition;
102        $(let details = $details;)?
103    }};
104}
105
106/// Assert that ``condition`` is true every time this function is called,
107/// **and** that it is called at least once. The corresponding test property
108/// will be viewable in the ``Antithesis SDK: Always`` group of your triage
109/// report.
110///
111/// # Example
112///
113/// ```
114/// use serde::Serialize;
115/// use antithesis_sdk::{assert_always, random};
116///
117/// // Details can be any Serialize type; non-null scalars and arrays are
118/// // wrapped under "value".
119/// // A borrowed struct avoids serialization on evaluations that are not emitted.
120/// #[derive(Serialize)]
121/// struct Details<'a> {
122///     max_allowed: u64,
123///     actual: u64,
124///     source: &'a str,
125/// }
126///
127/// const MAX_ALLOWED: u64 = 100;
128/// let actual = random::get_random() % 100u64;
129/// let details = Details { max_allowed: MAX_ALLOWED, actual, source: "demo" };
130/// antithesis_sdk::assert_always!(actual < MAX_ALLOWED, "Value in range", &details);
131/// ```
132///
133/// Ensure that non-const-evaluable messages are rejected.
134///
135/// ```
136/// use serde_json::json;
137/// const MESSAGE: &str = concat!("Value", " in range");
138/// antithesis_sdk::assert_always!(true, MESSAGE, &json!({}));
139/// ```
140///
141/// A message computed at runtime is rejected at compile time:
142///
143/// ```compile_fail
144/// use serde_json::json;
145/// let message = String::from("Value in range");
146/// antithesis_sdk::assert_always!(true, message, &json!({}));
147/// ```
148///
149/// ```compile_fail
150/// use serde_json::json;
151/// antithesis_sdk::assert_always!(true, format!("{}", "Value in range"), &json!({}));
152/// ```
153#[macro_export]
154macro_rules! assert_always {
155    ($condition:expr, $message:expr$(, $details:expr)?) => {
156        $crate::assert_helper!(
157            condition = $condition,
158            $message,
159            $(details = $details)?,
160            $crate::assert::AssertType::Always,
161            "Always",
162            must_hit = true
163        )
164    };
165    ($($rest:tt)*) => {
166        ::std::compile_error!(
167r#"Invalid syntax when calling macro `assert_always`.
168Example usage:
169    `assert_always!(condition_expr, "assertion message (const &'static str)", &details_json_value_expr)`
170"#
171        );
172    };
173}
174
175/// Assert that ``condition`` is true every time this function is called. The
176/// corresponding test property will pass even if the assertion is never
177/// encountered. This test property will be viewable in the
178/// ``Antithesis SDK: Always`` group of your triage report.
179///
180/// # Example
181///
182/// ```
183/// use serde::Serialize;
184/// use antithesis_sdk::{assert_always_or_unreachable, random};
185///
186/// // Only serialized when the assertion emits; see assert_always.
187/// #[derive(Serialize)]
188/// struct Details { max_allowed: u64, actual: u64 }
189///
190/// const MAX_ALLOWED: u64 = 100;
191/// let actual = random::get_random() % 100u64;
192/// let details = Details { max_allowed: MAX_ALLOWED, actual };
193/// antithesis_sdk::assert_always_or_unreachable!(actual < MAX_ALLOWED, "Value in range", &details);
194/// ```
195#[macro_export]
196macro_rules! assert_always_or_unreachable {
197    ($condition:expr, $message:expr$(, $details:expr)?) => {
198        $crate::assert_helper!(
199            condition = $condition,
200            $message,
201            $(details = $details)?,
202            $crate::assert::AssertType::Always,
203            "AlwaysOrUnreachable",
204            must_hit = false
205        )
206    };
207    ($($rest:tt)*) => {
208        ::std::compile_error!(
209r#"Invalid syntax when calling macro `assert_always_or_unreachable`.
210Example usage:
211    `assert_always_or_unreachable!(condition_expr, "assertion message (const &'static str)", &details_json_value_expr)`
212"#
213        );
214    };
215}
216
217/// Assert that ``condition`` is true at least one time that this function was
218/// called. (If the assertion is never encountered, the test property will
219/// therefore fail.) This test property will be viewable in the
220/// ``Antithesis SDK: Sometimes`` group.
221///
222/// # Example
223///
224/// ```
225/// use serde::Serialize;
226/// use antithesis_sdk::{assert_sometimes, random};
227///
228/// // Only serialized when the assertion emits; see assert_always.
229/// #[derive(Serialize)]
230/// struct Details { max_allowed: u64, actual: u64 }
231///
232/// const MAX_ALLOWED: u64 = 100;
233/// let actual = random::get_random() % 120u64;
234/// let details = Details { max_allowed: MAX_ALLOWED, actual };
235/// antithesis_sdk::assert_sometimes!(actual > MAX_ALLOWED, "Value in range", &details);
236/// ```
237#[macro_export]
238macro_rules! assert_sometimes {
239    ($condition:expr, $message:expr$(, $details:expr)?) => {
240        $crate::assert_helper!(
241            condition = $condition,
242            $message,
243            $(details = $details)?,
244            $crate::assert::AssertType::Sometimes,
245            "Sometimes",
246            must_hit = true
247        )
248    };
249    ($($rest:tt)*) => {
250        ::std::compile_error!(
251r#"Invalid syntax when calling macro `assert_sometimes`.
252Example usage:
253    `assert_sometimes!(condition_expr, "assertion message (const &'static str)", &details_json_value_expr)`
254"#
255        );
256    };
257}
258
259/// Assert that a line of code is reached at least once. The corresponding test
260/// property will pass if this macro is ever called. (If it is never called the
261/// test property will therefore fail.) This test property will be viewable in
262/// the ``Antithesis SDK: Reachablity assertions`` group.
263///
264/// # Example
265///
266/// ```
267/// use serde::Serialize;
268/// use antithesis_sdk::{assert_reachable, random};
269///
270/// // Only serialized when the assertion emits; see assert_always.
271/// #[derive(Serialize)]
272/// struct Details { max_allowed: u64, actual: u64 }
273///
274/// const MAX_ALLOWED: u64 = 100;
275/// let actual = random::get_random() % 120u64;
276/// let details = Details { max_allowed: MAX_ALLOWED, actual };
277/// if (actual > MAX_ALLOWED) {
278///     antithesis_sdk::assert_reachable!("Value in range", &details);
279/// }
280/// ```
281#[macro_export]
282macro_rules! assert_reachable {
283    ($message:expr$(, $details:expr)?) => {
284        $crate::assert_helper!(
285            condition = true,
286            $message,
287            $(details = $details)?,
288            $crate::assert::AssertType::Reachability,
289            "Reachable",
290            must_hit = true
291        )
292    };
293    ($($rest:tt)*) => {
294        ::std::compile_error!(
295r#"Invalid syntax when calling macro `assert_reachable`.
296Example usage:
297    `assert_reachable!("assertion message (const &'static str)", &details_json_value_expr)`
298"#
299        );
300    };
301}
302
303/// Assert that a line of code is never reached. The corresponding test property
304/// will fail if this macro is ever called. (If it is never called the test
305/// property will therefore pass.) This test property will be viewable in the
306/// ``Antithesis SDK: Reachablity assertions`` group.
307///
308/// # Example
309///
310/// ```
311/// use serde::Serialize;
312/// use antithesis_sdk::{assert_unreachable, random};
313///
314/// // Only serialized when the assertion emits; see assert_always.
315/// #[derive(Serialize)]
316/// struct Details { max_allowed: u64, actual: u64 }
317///
318/// const MAX_ALLOWED: u64 = 100;
319/// let actual = random::get_random() % 120u64;
320/// let details = Details { max_allowed: MAX_ALLOWED, actual };
321/// if (actual > 120u64) {
322///     antithesis_sdk::assert_unreachable!("Value is above range", &details);
323/// }
324/// ```
325#[macro_export]
326macro_rules! assert_unreachable {
327    ($message:expr$(, $details:expr)?) => {
328        $crate::assert_helper!(
329            condition = false,
330            $message,
331            $(details = $details)?,
332            $crate::assert::AssertType::Reachability,
333            "Unreachable",
334            must_hit = false
335        )
336    };
337    ($($rest:tt)*) => {
338        ::std::compile_error!(
339r#"Invalid syntax when calling macro `assert_unreachable`.
340Example usage:
341    `assert_unreachable!("assertion message (const &'static str)", &details_json_value_expr)`
342"#
343        );
344    };
345}
346
347/// Registers the guidance in the catalog and emits one hit with
348/// `$guidance_data`, an inspected `serde_json::Value`.
349#[cfg(feature = "full")]
350#[doc(hidden)]
351#[macro_export]
352macro_rules! guidance_helper {
353    ($guidance_type:expr, $message:expr, $maximize:literal, $guidance_data:expr) => {
354        // `$message` must be const evaluable
355        const _: &str = $message;
356
357        $crate::function!(FUN_NAME);
358
359        use $crate::assert::guidance::{GuidanceCatalogInfo, GuidanceType};
360        #[$crate::linkme::distributed_slice($crate::assert::ANTITHESIS_GUIDANCE_CATALOG)]
361        #[linkme(crate = $crate::linkme)] // Refer to our re-exported linkme.
362        static GUIDANCE_CATALOG_ITEM: GuidanceCatalogInfo = GuidanceCatalogInfo {
363            guidance_type: $guidance_type,
364            message: $message,
365            id: $message,
366            class: ::std::module_path!(),
367            function: &FUN_NAME,
368            file: ::std::file!(),
369            begin_line: ::std::line!(),
370            begin_column: ::std::column!(),
371            maximize: $maximize,
372        };
373
374        $crate::assert::guidance::guidance_impl(
375            $guidance_type,
376            $message,
377            $message,
378            ::std::module_path!(),
379            *Lazy::force(&FUN_NAME),
380            ::std::file!(),
381            ::std::line!(),
382            ::std::column!(),
383            $maximize,
384            $guidance_data,
385            true,
386        )
387    };
388}
389
390#[cfg(feature = "full")]
391#[doc(hidden)]
392#[macro_export]
393macro_rules! numeric_guidance_helper {
394    ($assert:path, $op:tt, $maximize:literal, $left:expr, $right:expr, $message:expr$(, $details:expr)?) => {{
395        let left = $left;
396        let right = $right;
397        let details = &$crate::serde_json::Value::Null;
398        $(let details = $details;)?
399        let details = $crate::details::WithGuidance {
400            details,
401            guidance: || $crate::details::NumericOperands { left: &left, right: &right },
402        };
403        $assert!(left $op right, $message, &details);
404
405        // Every `Diff` impl converts into f64
406        let diff = $crate::assert::guidance::Diff::diff(&left, right);
407        static GUARD: $crate::assert::guidance::Guard<$maximize> =
408            $crate::assert::guidance::Guard::init();
409        if $crate::assert::guidance::finite_operands(&left, &right, $message)
410            && GUARD.should_emit(diff) {
411            let operands = $crate::details::NumericOperands { left: &left, right: &right };
412            // Inspecting the operands is the only step that depends on their
413            // type; the packet is built and emitted inside the SDK crate.
414            if let Some(guidance_data) = $crate::details::guidance(&operands, $message) {
415                $crate::guidance_helper!($crate::assert::guidance::GuidanceType::Numeric, $message, $maximize, guidance_data);
416            }
417        }
418    }};
419}
420
421#[cfg(not(feature = "full"))]
422#[doc(hidden)]
423#[macro_export]
424macro_rules! numeric_guidance_helper {
425    ($assert:path, $op:tt, $maximize:literal, $left:expr, $right:expr, $message:expr$(, $details:expr)?) => {{
426        // `$message` must be const evaluable
427        const _: &str = $message;
428        $assert!($left $op $right, $message$(, $details)?);
429    }};
430}
431
432#[cfg(feature = "full")]
433#[doc(hidden)]
434#[macro_export]
435macro_rules! boolean_guidance_helper {
436    ($assert:path, $all:literal, {$($name:ident: $cond:expr),*}, $message:expr$(, $details:expr)?) => {{
437        let details = &$crate::serde_json::Value::Null;
438        $(let details = $details;)?
439        let (cond, guidance_data) = {
440            $(let $name = $cond;)*
441            (
442                if $all { true $(&& $name)* } else { false $(|| $name)* },
443                $crate::serde_json::json!({$(::std::stringify!($name): $name),*})
444            )
445        };
446        let details = $crate::details::WithGuidance {
447            details,
448            guidance: || guidance_data.clone(),
449        };
450        $assert!(cond, $message, &details);
451        // Built from `bool`s only, so the details inspection has nothing to
452        // find or replace in it: emitted as is.
453        $crate::guidance_helper!($crate::assert::guidance::GuidanceType::Boolean, $message, $all, guidance_data);
454    }};
455}
456
457#[cfg(not(feature = "full"))]
458#[doc(hidden)]
459#[macro_export]
460macro_rules! boolean_guidance_helper {
461    ($assert:path, $all:literal, {$($name:ident: $cond:expr),*}, $message:expr$(, $details:expr)?) => {{
462        let cond = {
463            $(let $name = $cond;)*
464            if $all { true $(&& $name)* } else { false $(|| $name)* }
465        };
466        $assert!(cond, $message$(, $details)?);
467    }};
468}
469
470/// `assert_always_greater_than(x, y, ...)` is mostly equivalent to
471/// `assert_always!(x > y, ...)`, except Antithesis has more visibility to the
472/// value of `x` and `y`, and the assertion details would be merged with
473/// `{"left": x, "right": y}`.
474///
475/// Ensure that non-const-evaluable messages are rejected.
476///
477/// ```
478/// use serde_json::json;
479/// const MESSAGE: &str = concat!("x", " over y");
480/// antithesis_sdk::assert_always_greater_than!(2, 1, MESSAGE, &json!({}));
481/// ```
482///
483/// ```compile_fail
484/// use serde_json::json;
485/// antithesis_sdk::assert_always_greater_than!(2, 1, format!("{}", "x over y"), &json!({}));
486/// ```
487#[macro_export]
488macro_rules! assert_always_greater_than {
489    ($left:expr, $right:expr, $message:expr$(, $details:expr)?) => {
490        $crate::numeric_guidance_helper!($crate::assert_always, >, false, $left, $right, $message$(, $details)?)
491    };
492    ($($rest:tt)*) => {
493        ::std::compile_error!(
494r#"Invalid syntax when calling macro `assert_always_greater_than`.
495Example usage:
496    `assert_always_greater_than!(left_expr, right_expr, "assertion message (const &'static str)", &details_json_value_expr)`
497"#
498        );
499    };
500}
501
502/// `assert_always_greater_than_or_equal_to(x, y, ...)` is mostly equivalent to
503/// `assert_always!(x >= y, ...)`, except Antithesis has more visibility to the
504/// value of `x` and `y`, and the assertion details would be merged with
505/// `{"left": x, "right": y}`.
506#[macro_export]
507macro_rules! assert_always_greater_than_or_equal_to {
508    ($left:expr, $right:expr, $message:expr$(, $details:expr)?) => {
509        $crate::numeric_guidance_helper!($crate::assert_always, >=, false, $left, $right, $message$(, $details)?)
510    };
511    ($($rest:tt)*) => {
512        ::std::compile_error!(
513r#"Invalid syntax when calling macro `assert_always_greater_than_or_equal_to`.
514Example usage:
515    `assert_always_greater_than_or_equal_to!(left_expr, right_expr, "assertion message (const &'static str)", &details_json_value_expr)`
516"#
517        );
518    };
519}
520
521/// `assert_always_less_than(x, y, ...)` is mostly equivalent to
522/// `assert_always!(x < y, ...)`, except Antithesis has more visibility to the
523/// value of `x` and `y`, and the assertion details would be merged with
524/// `{"left": x, "right": y}`.
525#[macro_export]
526macro_rules! assert_always_less_than {
527    ($left:expr, $right:expr, $message:expr$(, $details:expr)?) => {
528        $crate::numeric_guidance_helper!($crate::assert_always, <, true, $left, $right, $message$(, $details)?)
529    };
530    ($($rest:tt)*) => {
531        ::std::compile_error!(
532r#"Invalid syntax when calling macro `assert_always_less_than`.
533Example usage:
534    `assert_always_less_than!(left_expr, right_expr, "assertion message (const &'static str)", &details_json_value_expr)`
535"#
536        );
537    };
538}
539
540/// `assert_always_less_than_or_equal_to(x, y, ...)` is mostly equivalent to
541/// `assert_always!(x <= y, ...)`, except Antithesis has more visibility to the
542/// value of `x` and `y`, and the assertion details would be merged with
543/// `{"left": x, "right": y}`.
544#[macro_export]
545macro_rules! assert_always_less_than_or_equal_to {
546    ($left:expr, $right:expr, $message:expr$(, $details:expr)?) => {
547        $crate::numeric_guidance_helper!($crate::assert_always, <=, true, $left, $right, $message$(, $details)?)
548    };
549    ($($rest:tt)*) => {
550        ::std::compile_error!(
551r#"Invalid syntax when calling macro `assert_always_less_than_or_equal_to`.
552Example usage:
553    `assert_always_less_than_or_equal_to!(left_expr, right_expr, "assertion message (const &'static str)", &details_json_value_expr)`
554"#
555        );
556    };
557}
558
559/// `assert_sometimes_greater_than(x, y, ...)` is mostly equivalent to
560/// `assert_sometimes!(x > y, ...)`, except Antithesis has more visibility to
561/// the value of `x` and `y`, and the assertion details would be merged with
562/// `{"left": x, "right": y}`.
563#[macro_export]
564macro_rules! assert_sometimes_greater_than {
565    ($left:expr, $right:expr, $message:expr$(, $details:expr)?) => {
566        $crate::numeric_guidance_helper!($crate::assert_sometimes, >, true, $left, $right, $message$(, $details)?)
567    };
568    ($($rest:tt)*) => {
569        ::std::compile_error!(
570r#"Invalid syntax when calling macro `assert_sometimes_greater_than`.
571Example usage:
572    `assert_sometimes_greater_than!(left_expr, right_expr, "assertion message (const &'static str)", &details_json_value_expr)`
573"#
574        );
575    };
576}
577
578/// `assert_sometimes_greater_than_or_equal_to(x, y, ...)` is mostly equivalent
579/// to `assert_sometimes!(x >= y, ...)`, except Antithesis has more visibility
580/// to the value of `x` and `y`, and the assertion details would be merged with
581/// `{"left": x, "right": y}`.
582#[macro_export]
583macro_rules! assert_sometimes_greater_than_or_equal_to {
584    ($left:expr, $right:expr, $message:expr$(, $details:expr)?) => {
585        $crate::numeric_guidance_helper!($crate::assert_sometimes, >=, true, $left, $right, $message$(, $details)?)
586    };
587    ($($rest:tt)*) => {
588        ::std::compile_error!(
589r#"Invalid syntax when calling macro `assert_sometimes_greater_than_or_equal_to`.
590Example usage:
591    `assert_sometimes_greater_than_or_equal_to!(left_expr, right_expr, "assertion message (const &'static str)", &details_json_value_expr)`
592"#
593        );
594    };
595}
596
597/// `assert_sometimes_less_than(x, y, ...)` is mostly equivalent to
598/// `assert_sometimes!(x < y, ...)`, except Antithesis has more visibility to
599/// the value of `x` and `y`, and the assertion details would be merged with
600/// `{"left": x, "right": y}`.
601#[macro_export]
602macro_rules! assert_sometimes_less_than {
603    ($left:expr, $right:expr, $message:expr$(, $details:expr)?) => {
604        $crate::numeric_guidance_helper!($crate::assert_sometimes, <, false, $left, $right, $message$(, $details)?)
605    };
606    ($($rest:tt)*) => {
607        ::std::compile_error!(
608r#"Invalid syntax when calling macro `assert_sometimes_less_than`.
609Example usage:
610    `assert_sometimes_less_than!(left_expr, right_expr, "assertion message (const &'static str)", &details_json_value_expr)`
611"#
612        );
613    };
614}
615
616/// `assert_sometimes_less_than_or_equal_to(x, y, ...)` is mostly equivalent to
617/// `assert_sometimes!(x <= y, ...)`, except Antithesis has more visibility to
618/// the value of `x` and `y`, and the assertion details would be merged with
619/// `{"left": x, "right": y}`.
620#[macro_export]
621macro_rules! assert_sometimes_less_than_or_equal_to {
622    ($left:expr, $right:expr, $message:expr$(, $details:expr)?) => {
623        $crate::numeric_guidance_helper!($crate::assert_sometimes, <=, false, $left, $right, $message$(, $details)?)
624    };
625    ($($rest:tt)*) => {
626        ::std::compile_error!(
627r#"Invalid syntax when calling macro `assert_sometimes_less_than_or_equal_to`.
628Example usage:
629    `assert_sometimes_less_than_or_equal_to!(left_expr, right_expr, "assertion message (const &'static str)", &details_json_value_expr)`
630"#
631        );
632    };
633}
634
635/// `assert_always_some({a: x, b: y, ...})` is similar to `assert_always(x || y
636/// || ...)`, except:
637/// - Antithesis has more visibility to the individual propositions.
638/// - There is no short-circuiting, so all of `x`, `y`, ... would be evaluated.
639/// - The assertion details would be merged with `{"a": x, "b": y, ...}`.
640///
641/// Ensure that non-const-evaluable messages are rejected.
642///
643/// ```
644/// use serde_json::json;
645/// const MESSAGE: &str = concat!("at least ", "one");
646/// antithesis_sdk::assert_always_some!({a: true, b: false}, MESSAGE, &json!({}));
647/// ```
648///
649/// ```compile_fail
650/// use serde_json::json;
651/// antithesis_sdk::assert_always_some!({a: true, b: false}, format!("{}", "at least one"), &json!({}));
652/// ```
653#[macro_export]
654macro_rules! assert_always_some {
655    ({$($($name:ident: $cond:expr),+ $(,)?)?}, $message:expr$(, $details:expr)?) => {
656        $crate::boolean_guidance_helper!($crate::assert_always, false, {$($($name: $cond),+)?}, $message$(, $details)?);
657    };
658    ($($rest:tt)*) => {
659        ::std::compile_error!(
660r#"Invalid syntax when calling macro `assert_always_some`.
661Example usage:
662    `assert_always_some!({field1: cond1, field2: cond2, ...}, "assertion message (const &'static str)", &details_json_value_expr)`
663"#
664        );
665    };
666}
667
668/// `assert_sometimes_all({a: x, b: y, ...})` is similar to
669/// `assert_sometimes(x && y && ...)`, except:
670/// - Antithesis has more visibility to the individual propositions.
671/// - There is no short-circuiting, so all of `x`, `y`, ... would be evaluated.
672/// - The assertion details would be merged with `{"a": x, "b": y, ...}`.
673#[macro_export]
674macro_rules! assert_sometimes_all {
675    ({$($($name:ident: $cond:expr),+ $(,)?)?}, $message:expr$(, $details:expr)?) => {
676        $crate::boolean_guidance_helper!($crate::assert_sometimes, true, {$($($name: $cond),+)?}, $message$(, $details)?);
677    };
678    ($($rest:tt)*) => {
679        ::std::compile_error!(
680r#"Invalid syntax when calling macro `assert_sometimes_all`.
681Example usage:
682    `assert_sometimes_all!({field1: cond1, field2: cond2, ...}, "assertion message (const &'static str)", &details_json_value_expr)`
683"#
684        );
685    };
686}