quantor/macros/
debug.rs

1//! # quantor: Debug Macros
2//!
3//! This module defines debug-only macros for evaluating logical quantifiers and assertions without
4//! affecting release builds. These macros are compiled only when the `debug-tools` feature is enabled.
5//!
6//! ## Debug Assertion Macros
7//! These macros are the debug-only equivalents of the main assertion macros. They panic on failure,
8//! but only in debug builds:
9//!
10//! - [`debug_assert_forall!`] – Asserts all elements satisfy a predicate.
11//! - [`debug_assert_exists!`] – Asserts that at least one element satisfies a predicate.
12//! - [`debug_assert_none!`] – Asserts that no elements satisfy a predicate.
13//! - [`debug_assert_pairwise!`] – Asserts a binary predicate holds for all adjacent pairs.
14//! - [`debug_assert_unique!`] – Asserts that all elements are unique.
15//! - [`debug_assert_duplicates!`] – Asserts that duplicates exist.
16//!
17//! ## Debug Inspection Macros
18//! These macros do **not panic**; instead, they log failing elements or conditions for inspection,
19//! and are useful for fuzzing, diagnostics, and non-blocking testing:
20//!
21//! - [`debug_forall!`] – Logs elements that violate a predicate.
22//! - [`debug_exists!`] – Logs if no elements match.
23//! - [`debug_none!`] – Logs which elements unexpectedly match.
24//! - [`debug_pairwise!`] – Logs failing adjacent pairs.
25//! - [`debug_unique!`] – Logs repeated elements.
26//! - [`debug_duplicates!`] – Logs all detected duplicates.
27
28/// Debug-only version of [`assert_forall!`](crate::assert_forall).
29/// Panics if any element fails the predicate, but only in debug builds.
30#[cfg(feature = "debug-tools")]
31#[macro_export]
32macro_rules! debug_assert_forall {
33    ($xs:expr, $pred:expr) => {
34        #[cfg(debug_assertions)]
35        match $crate::quantifiers::basic::forall($xs, $pred) {
36            Ok(()) => {},
37            Err(e) => panic!("debug_assert_forall! failed: {}", e),
38        }
39    };
40}
41
42/// Logs all elements that fail the predicate.
43/// Does not panic. Active only in debug builds.
44#[cfg(feature = "debug-tools")]
45#[macro_export]
46macro_rules! debug_forall {
47    ($xs:expr, $pred:expr) => {
48        #[cfg(debug_assertions)]
49        {
50            let failed: Vec<_> = $xs.into_iter().filter(|x| !$pred(x)).collect();
51            if !failed.is_empty() {
52                println!(
53                    "[debug_forall] {} element(s) failed: {:?}",
54                    failed.len(),
55                    failed
56                );
57            }
58        }
59    };
60}
61
62/// Debug-only version of [`assert_exists!`](crate::assert_exists).
63/// Panics if no element satisfies the predicate, but only in debug builds.
64#[cfg(feature = "debug-tools")]
65#[macro_export]
66macro_rules! debug_assert_exists {
67    ($xs:expr, $pred:expr) => {
68        #[cfg(debug_assertions)]
69        match $crate::quantifiers::basic::exists($xs, $pred) {
70            Ok(()) => {},
71            Err(e) => panic!("debug_assert_exists! failed: {}", e),
72        }
73    };
74}
75
76/// Logs a message if no element matches the predicate.
77/// Does not panic. Active only in debug builds.
78#[cfg(feature = "debug-tools")]
79#[macro_export]
80macro_rules! debug_exists {
81    ($xs:expr, $pred:expr) => {
82        #[cfg(debug_assertions)]
83        if !$crate::quantifiers::basic::exists($xs, $pred).is_ok() {
84            println!("[debug_exists] no matching element found.");
85        }
86    };
87}
88
89/// Debug-only version of [`assert_exactly_n!`](crate::assert_exactly_n).
90/// Panics if there are more or less elements which satisfy the predicate than expected, but only in debug builds.
91#[cfg(feature = "debug-tools")]
92#[macro_export]
93macro_rules! debug_assert_exactly_n {
94    ($iter:expr, $count:expr, $pred:expr) => {{
95        #[cfg(debug_assertions)]
96        match $crate::quantifiers::basic::exactly_n($iter, $count, $pred) {
97            Ok(()) => {},
98            Err(e) => panic!("assert_exactly_n! failed: {}", e),
99        }
100    }};
101}
102
103/// Logs a message if the number of matching elements does not equal the expected count.
104/// Does not panic. Active only in debug builds.
105#[cfg(feature = "debug-tools")]
106#[macro_export]
107macro_rules! debug_exactly_n {
108    ($xs:expr, $count:expr, $pred:expr) => {
109        #[cfg(debug_assertions)]
110        {
111            match $crate::quantifiers::basic::exactly_n($xs, $count, $pred) {
112                Ok(()) => {},
113                Err(e) => {println!("[debug_exactly_n] {}", e)},
114            }
115        }
116    };
117}
118
119/// Debug-only version of [`assert_none!`](crate::assert_none).
120/// Panics if any element satisfies the predicate, but only in debug builds.
121#[cfg(feature = "debug-tools")]
122#[macro_export]
123macro_rules! debug_assert_none {
124    ($xs:expr, $pred:expr) => {
125        #[cfg(debug_assertions)]
126        match $crate::quantifiers::basic::none($xs, $pred) {
127            Ok(()) => {},
128            Err(e) => panic!("debug_assert_none! failed: {}", e),
129        }
130    };
131}
132
133/// Logs all elements that unexpectedly match the predicate.
134/// Does not panic. Active only in debug builds.
135#[cfg(feature = "debug-tools")]
136#[macro_export]
137macro_rules! debug_none {
138    ($xs:expr, $pred:expr) => {
139        #[cfg(debug_assertions)]
140        {
141            let matched: Vec<_> = $xs.into_iter().filter(|x| $pred(x)).collect();
142            if !matched.is_empty() {
143                println!(
144                    "[debug_none] {} element(s) unexpectedly matched: {:?}",
145                    matched.len(),
146                    matched
147                );
148            }
149        }
150    };
151}
152
153/// Debug-only version of [`assert_duplicates!`](crate::assert_duplicates).
154/// Panics if no duplicates are found, but only in debug builds.
155#[cfg(feature = "debug-tools")]
156#[macro_export]
157macro_rules! debug_assert_duplicates {
158    ($xs:expr) => {
159        #[cfg(debug_assertions)]
160        {
161            let candidates = $crate::quantifiers::selection::select_duplicates($xs);
162
163            if candidates.is_empty() {
164                panic!("debug_assert_duplicates! failed: {:#?}", candidates)
165            }
166        }
167    };
168}
169
170/// Logs all duplicate elements in the sequence.
171/// Does not panic. Active only in debug builds.
172#[cfg(feature = "debug-tools")]
173#[macro_export]
174macro_rules! debug_duplicates {
175    ($xs:expr) => {
176        #[cfg(debug_assertions)]
177        {
178            use std::collections::HashSet;
179            let mut seen = HashSet::new();
180            let mut dups = HashSet::new();
181            for x in $xs {
182                if !seen.insert(x) {
183                    dups.insert(x);
184                }
185            }
186            if !dups.is_empty() {
187                println!("[debug_duplicates] found duplicates: {:?}", dups);
188            }
189        }
190    };
191}
192
193/// Debug-only version of [`assert_unique!`](crate::assert_unique).
194/// Panics if any duplicates are found, but only in debug builds.
195#[cfg(feature = "debug-tools")]
196#[macro_export]
197macro_rules! debug_assert_unique {
198    ($xs:expr) => {
199        #[cfg(debug_assertions)]
200        {
201            let candidates = $crate::quantifiers::selection::select_duplicates($xs);
202
203            if !candidates.is_empty() {
204                panic!("debug_assert_unique! failed: {:#?}", candidates)
205            }
206        }
207    };
208}
209
210/// Logs all repeated elements in the sequence.
211/// Does not panic. Active only in debug builds.
212#[cfg(feature = "debug-tools")]
213#[macro_export]
214macro_rules! debug_unique {
215    ($xs:expr) => {
216        #[cfg(debug_assertions)]
217        {
218            use std::collections::HashSet;
219            let mut seen = HashSet::new();
220            let mut repeated = Vec::new();
221            
222            for x in $xs {
223                if !seen.insert(x) {
224                    repeated.push(x);
225                }
226            }
227            if !repeated.is_empty() {
228                println!(
229                    "[debug_unique] violated: found duplicate(s): {:?}",
230                    repeated
231                );
232            }
233        }
234    };
235}
236
237
238/// Debug-only version of [`assert_pairwise!`](crate::assert_pairwise).
239/// Panics if any adjacent pair fails the predicate, but only in debug builds.
240#[cfg(feature = "debug-tools")]
241#[macro_export]
242macro_rules! debug_assert_pairwise {
243    ($xs:expr, $pred:expr) => {
244        #[cfg(debug_assertions)]
245        match $crate::quantifiers::structured::pairwise($xs, $pred) {
246            Ok(()) => {},
247            Err(e) => panic!("debug_assert_pairwise! failed: {}", e),
248        }
249    };
250}
251
252/// Logs all adjacent pairs that violate the predicate.
253/// Does not panic. Active only in debug builds.
254#[cfg(feature = "debug-tools")]
255#[macro_export]
256macro_rules! debug_pairwise {
257    ($xs:expr, $pred:expr) => {
258        #[cfg(debug_assertions)]
259        {
260            let mut failed = Vec::new();
261            let mut iter = $xs.into_iter();
262            if let Some(mut prev) = iter.next() {
263                for curr in iter {
264                    if !$pred(prev, curr) {
265                        failed.push((prev, curr));
266                    }
267                    prev = curr;
268                }
269            }
270            if !failed.is_empty() {
271                println!(
272                    "[debug_pairwise] {} pair(s) failed: {:?}",
273                    failed.len(),
274                    failed
275                );
276            }
277        }
278    };
279}