Skip to main content

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}