Skip to main content

qubit_function/tester/
box_tester.rs

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