qubit_function/tester/box_tester.rs
1/*******************************************************************************
2 *
3 * Copyright (c) 2025 - 2026.
4 * Haixing Hu, Qubit Co. Ltd.
5 *
6 * All rights reserved.
7 *
8 ******************************************************************************/
9//! Defines the `BoxTester` public type.
10
11#![allow(unused_imports)]
12
13use super::*;
14
15// ============================================================================
16// BoxTester: Single Ownership Implementation
17// ============================================================================
18
19/// Single ownership Tester implemented using `Box`
20///
21/// `BoxTester` wraps a closure in `Box<dyn Fn() -> bool>`, providing single
22/// ownership semantics with no additional allocation overhead beyond the
23/// initial boxing.
24///
25/// # Characteristics
26///
27/// - **Single ownership**: Cannot be cloned
28/// - **Zero overhead**: Single heap allocation
29/// - **Consuming combination**: `and()`/`or()`/`not()` consume `self`
30/// - **Type flexibility**: Accepts any `Tester` implementation
31///
32/// # Use Cases
33///
34/// - One-time testing scenarios
35/// - Builder patterns requiring ownership transfer
36/// - Simple state checking without sharing
37/// - Chained calls with ownership transfer
38///
39/// # Examples
40///
41/// ```rust
42/// use qubit_function::{BoxTester, Tester};
43/// use std::sync::{Arc, atomic::{AtomicUsize, Ordering}};
44///
45/// // State managed externally
46/// let count = Arc::new(AtomicUsize::new(0));
47/// let count_clone = Arc::clone(&count);
48///
49/// let tester = BoxTester::new(move || {
50/// count_clone.load(Ordering::Relaxed) < 3
51/// });
52///
53/// assert!(tester.test());
54/// count.fetch_add(1, Ordering::Relaxed);
55/// assert!(tester.test());
56/// count.fetch_add(1, Ordering::Relaxed);
57/// assert!(tester.test());
58/// count.fetch_add(1, Ordering::Relaxed);
59/// assert!(!tester.test());
60///
61/// // Logical combination
62/// let combined = BoxTester::new(|| true)
63/// .and(|| false)
64/// .or(|| true);
65/// assert!(combined.test());
66/// ```
67///
68/// # Author
69///
70/// Haixing Hu
71pub struct BoxTester {
72 pub(super) function: Box<dyn Fn() -> bool>,
73}
74
75impl BoxTester {
76 /// Creates a new `BoxTester` from a closure
77 ///
78 /// # Type Parameters
79 ///
80 /// * `F` - Closure type implementing `Fn() -> bool`
81 ///
82 /// # Parameters
83 ///
84 /// * `f` - The closure to wrap
85 ///
86 /// # Return Value
87 ///
88 /// A new `BoxTester` instance
89 ///
90 /// # Examples
91 ///
92 /// ```rust
93 /// use qubit_function::BoxTester;
94 ///
95 /// let tester = BoxTester::new(|| true);
96 /// ```
97 #[inline]
98 pub fn new<F>(f: F) -> Self
99 where
100 F: Fn() -> bool + 'static,
101 {
102 BoxTester {
103 function: Box::new(f),
104 }
105 }
106
107 /// Combines this tester with another tester using logical AND
108 ///
109 /// Returns a new `BoxTester` that returns `true` only when both tests
110 /// pass. Short-circuit evaluation: if the first test fails, the second
111 /// will not be executed.
112 ///
113 /// # Type Parameters
114 ///
115 /// * `T` - Type implementing `Tester`
116 ///
117 /// # Parameters
118 ///
119 /// * `next` - The tester to combine with
120 ///
121 /// # Return Value
122 ///
123 /// A new `BoxTester` representing logical AND
124 ///
125 /// # Examples
126 ///
127 /// ```rust
128 /// use qubit_function::{BoxTester, Tester};
129 /// use std::sync::{Arc, atomic::{AtomicUsize, AtomicBool, Ordering}};
130 ///
131 /// // Simulate service status
132 /// let request_count = Arc::new(AtomicUsize::new(0));
133 /// let is_available = Arc::new(AtomicBool::new(true));
134 /// let max_requests = 1000;
135 ///
136 /// let count_clone = Arc::clone(&request_count);
137 /// let available_clone = Arc::clone(&is_available);
138 ///
139 /// // Service available and request count not exceeded
140 /// let service_ok = BoxTester::new(move || {
141 /// available_clone.load(Ordering::Relaxed)
142 /// })
143 /// .and(move || {
144 /// count_clone.load(Ordering::Relaxed) < max_requests
145 /// });
146 ///
147 /// // Initial state: available and request count 0
148 /// assert!(service_ok.test());
149 ///
150 /// // Simulate request increase
151 /// request_count.store(500, Ordering::Relaxed);
152 /// assert!(service_ok.test());
153 ///
154 /// // Request count exceeded
155 /// request_count.store(1500, Ordering::Relaxed);
156 /// assert!(!service_ok.test());
157 ///
158 /// // Service unavailable
159 /// is_available.store(false, Ordering::Relaxed);
160 /// assert!(!service_ok.test());
161 /// ```
162 #[inline]
163 pub fn and<T>(self, next: T) -> BoxTester
164 where
165 T: Tester + 'static,
166 {
167 let self_fn = self.function;
168 let next_tester = next;
169 BoxTester::new(move || self_fn() && next_tester.test())
170 }
171
172 /// Combines this tester with another tester using logical OR
173 ///
174 /// Returns a new `BoxTester` that returns `true` if either test passes.
175 /// Short-circuit evaluation: if the first test passes, the second will
176 /// not be executed.
177 ///
178 /// # Type Parameters
179 ///
180 /// * `T` - Type implementing `Tester`
181 ///
182 /// # Parameters
183 ///
184 /// * `next` - The tester to combine with
185 ///
186 /// # Return Value
187 ///
188 /// A new `BoxTester` representing logical OR
189 ///
190 /// # Examples
191 ///
192 /// ```rust
193 /// use qubit_function::{BoxTester, Tester};
194 /// use std::sync::{Arc, atomic::{AtomicUsize, AtomicBool, Ordering}};
195 ///
196 /// // Simulate service status
197 /// let request_count = Arc::new(AtomicUsize::new(0));
198 /// let is_healthy = Arc::new(AtomicBool::new(true));
199 /// let max_requests = 100;
200 ///
201 /// let count_clone = Arc::clone(&request_count);
202 /// let health_clone = Arc::clone(&is_healthy);
203 ///
204 /// // Service healthy or low request count
205 /// let can_serve = BoxTester::new(move || {
206 /// health_clone.load(Ordering::Relaxed)
207 /// })
208 /// .or(move || {
209 /// count_clone.load(Ordering::Relaxed) < max_requests
210 /// });
211 ///
212 /// // Initial state: healthy and request count 0
213 /// assert!(can_serve.test());
214 ///
215 /// // Request count increased but within limit
216 /// request_count.store(50, Ordering::Relaxed);
217 /// assert!(can_serve.test());
218 ///
219 /// // Request count exceeded but service healthy
220 /// request_count.store(150, Ordering::Relaxed);
221 /// assert!(can_serve.test()); // still healthy
222 ///
223 /// // Service unhealthy but low request count
224 /// is_healthy.store(false, Ordering::Relaxed);
225 /// request_count.store(50, Ordering::Relaxed);
226 /// assert!(can_serve.test()); // low request count
227 ///
228 /// // Unhealthy and high request count
229 /// request_count.store(150, Ordering::Relaxed);
230 /// assert!(!can_serve.test());
231 /// ```
232 #[inline]
233 pub fn or<T>(self, next: T) -> BoxTester
234 where
235 T: Tester + 'static,
236 {
237 let self_fn = self.function;
238 let next_tester = next;
239 BoxTester::new(move || self_fn() || next_tester.test())
240 }
241
242 /// Negates the result of this tester
243 ///
244 /// Returns a new `BoxTester` that returns the opposite value of the
245 /// original test result.
246 ///
247 /// # Return Value
248 ///
249 /// A new `BoxTester` representing logical NOT
250 ///
251 /// # Examples
252 ///
253 /// ```rust
254 /// use qubit_function::{BoxTester, Tester};
255 /// use std::sync::{Arc, atomic::{AtomicUsize, Ordering}};
256 ///
257 /// // Simulate resource usage
258 /// let memory_usage = Arc::new(AtomicUsize::new(0));
259 /// let max_memory = 1024; // MB
260 ///
261 /// let memory_clone = Arc::clone(&memory_usage);
262 ///
263 /// // Memory usage not exceeded
264 /// let memory_ok = BoxTester::new(move || {
265 /// memory_clone.load(Ordering::Relaxed) <= max_memory
266 /// });
267 ///
268 /// // Initial state: normal memory usage
269 /// memory_usage.store(512, Ordering::Relaxed);
270 /// assert!(memory_ok.test());
271 ///
272 /// // Memory usage exceeded (negated)
273 /// let memory_critical = memory_ok.not();
274 /// assert!(!memory_critical.test());
275 ///
276 /// // Memory usage exceeded
277 /// memory_usage.store(2048, Ordering::Relaxed);
278 /// assert!(memory_critical.test());
279 /// ```
280 #[allow(clippy::should_implement_trait)]
281 #[inline]
282 pub fn not(self) -> BoxTester {
283 let self_fn = self.function;
284 BoxTester::new(move || !self_fn())
285 }
286
287 /// Combines this tester with another tester using logical NAND
288 ///
289 /// Returns a new `BoxTester` that returns `true` unless both tests pass.
290 /// Equivalent to `!(self AND other)`.
291 ///
292 /// # Type Parameters
293 ///
294 /// * `T` - Type implementing `Tester`
295 ///
296 /// # Parameters
297 ///
298 /// * `next` - The tester to combine with
299 ///
300 /// # Return Value
301 ///
302 /// A new `BoxTester` representing logical NAND
303 ///
304 /// # Examples
305 ///
306 /// ```rust
307 /// use qubit_function::{BoxTester, Tester};
308 /// use std::sync::{Arc, atomic::{AtomicBool, Ordering}};
309 ///
310 /// let flag1 = Arc::new(AtomicBool::new(true));
311 /// let flag2 = Arc::new(AtomicBool::new(true));
312 ///
313 /// let flag1_clone = Arc::clone(&flag1);
314 /// let flag2_clone = Arc::clone(&flag2);
315 ///
316 /// let nand = BoxTester::new(move || {
317 /// flag1_clone.load(Ordering::Relaxed)
318 /// })
319 /// .nand(move || {
320 /// flag2_clone.load(Ordering::Relaxed)
321 /// });
322 ///
323 /// // Both true returns false
324 /// assert!(!nand.test());
325 ///
326 /// // At least one false returns true
327 /// flag1.store(false, Ordering::Relaxed);
328 /// assert!(nand.test());
329 /// ```
330 #[inline]
331 pub fn nand<T>(self, next: T) -> BoxTester
332 where
333 T: Tester + 'static,
334 {
335 let self_fn = self.function;
336 let next_tester = next;
337 BoxTester::new(move || !(self_fn() && next_tester.test()))
338 }
339
340 /// Combines this tester with another tester using logical XOR
341 ///
342 /// Returns a new `BoxTester` that returns `true` if exactly one test
343 /// passes.
344 ///
345 /// # Type Parameters
346 ///
347 /// * `T` - Type implementing `Tester`
348 ///
349 /// # Parameters
350 ///
351 /// * `next` - The tester to combine with
352 ///
353 /// # Return Value
354 ///
355 /// A new `BoxTester` representing logical XOR
356 ///
357 /// # Examples
358 ///
359 /// ```rust
360 /// use qubit_function::{BoxTester, Tester};
361 /// use std::sync::{Arc, atomic::{AtomicBool, Ordering}};
362 ///
363 /// let flag1 = Arc::new(AtomicBool::new(true));
364 /// let flag2 = Arc::new(AtomicBool::new(false));
365 ///
366 /// let flag1_clone1 = Arc::clone(&flag1);
367 /// let flag2_clone1 = Arc::clone(&flag2);
368 ///
369 /// let xor = BoxTester::new(move || {
370 /// flag1_clone1.load(Ordering::Relaxed)
371 /// })
372 /// .xor(move || {
373 /// flag2_clone1.load(Ordering::Relaxed)
374 /// });
375 ///
376 /// // One true one false returns true
377 /// assert!(xor.test());
378 ///
379 /// // Both true returns false
380 /// flag2.store(true, Ordering::Relaxed);
381 /// assert!(!xor.test());
382 ///
383 /// // Both false returns false
384 /// flag1.store(false, Ordering::Relaxed);
385 /// flag2.store(false, Ordering::Relaxed);
386 /// assert!(!xor.test());
387 /// ```
388 #[inline]
389 pub fn xor<T>(self, next: T) -> BoxTester
390 where
391 T: Tester + 'static,
392 {
393 let self_fn = self.function;
394 let next_tester = next;
395 BoxTester::new(move || self_fn() ^ next_tester.test())
396 }
397
398 /// Combines this tester with another tester using logical NOR
399 ///
400 /// Returns a new `BoxTester` that returns `true` only when both tests
401 /// fail. Equivalent to `!(self OR other)`.
402 ///
403 /// # Type Parameters
404 ///
405 /// * `T` - Type implementing `Tester`
406 ///
407 /// # Parameters
408 ///
409 /// * `next` - The tester to combine with
410 ///
411 /// # Return Value
412 ///
413 /// A new `BoxTester` representing logical NOR
414 ///
415 /// # Examples
416 ///
417 /// ```rust
418 /// use qubit_function::{BoxTester, Tester};
419 /// use std::sync::{Arc, atomic::{AtomicBool, Ordering}};
420 ///
421 /// let flag1 = Arc::new(AtomicBool::new(false));
422 /// let flag2 = Arc::new(AtomicBool::new(false));
423 ///
424 /// let flag1_clone = Arc::clone(&flag1);
425 /// let flag2_clone = Arc::clone(&flag2);
426 ///
427 /// let nor = BoxTester::new(move || {
428 /// flag1_clone.load(Ordering::Relaxed)
429 /// })
430 /// .nor(move || {
431 /// flag2_clone.load(Ordering::Relaxed)
432 /// });
433 ///
434 /// // Both false returns true
435 /// assert!(nor.test());
436 ///
437 /// // At least one true returns false
438 /// flag1.store(true, Ordering::Relaxed);
439 /// assert!(!nor.test());
440 /// ```
441 #[inline]
442 pub fn nor<T>(self, next: T) -> BoxTester
443 where
444 T: Tester + 'static,
445 {
446 let self_fn = self.function;
447 let next_tester = next;
448 BoxTester::new(move || !(self_fn() || next_tester.test()))
449 }
450}
451
452impl Tester for BoxTester {
453 #[inline]
454 fn test(&self) -> bool {
455 (self.function)()
456 }
457
458 #[inline]
459 fn into_box(self) -> BoxTester {
460 self
461 }
462
463 #[inline]
464 fn into_rc(self) -> RcTester {
465 let func = self.function;
466 RcTester {
467 function: Rc::new(func),
468 }
469 }
470
471 // Note: BoxTester does not implement Send + Sync, so into_arc()
472 // cannot be implemented. Calling into_arc() on BoxTester will result
473 // in a compile error due to the Send + Sync trait bounds not being
474 // satisfied. The default Tester trait implementation will be used.
475
476 #[inline]
477 fn into_fn(self) -> impl Fn() -> bool {
478 self.function
479 }
480
481 // Note: BoxTester does not implement Clone, so to_box(), to_rc(),
482 // to_arc(), and to_fn() cannot be implemented. Calling these methods
483 // on BoxTester will result in a compile error due to the Clone trait
484 // bound not being satisfied. The default Tester trait implementations
485 // will be used.
486}