Skip to main content

qubit_function/tester/
arc_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 `ArcTester` public type.
12
13use std::ops::Not;
14
15use super::{
16    Arc,
17    BoxTester,
18    Rc,
19    RcTester,
20    Tester,
21};
22
23// ============================================================================
24// ArcTester: Thread-Safe Shared Ownership Implementation
25// ============================================================================
26
27/// Thread-safe shared ownership Tester implemented using `Arc`
28///
29/// `ArcTester` wraps a closure in `Arc<dyn Fn() -> bool + Send + Sync>`,
30/// allowing the tester to be cloned and safely shared across threads.
31///
32/// # Characteristics
33///
34/// - **Shared ownership**: Can be cloned
35/// - **Thread-safe**: Can be sent across threads
36/// - **Lock-free overhead**: Uses `Fn` without needing `Mutex`
37/// - **Borrowing combination**: `and()`/`or()` borrow `&self`
38///
39/// # Use Cases
40///
41/// - Multi-threaded testing scenarios
42/// - Health checks shared across threads
43/// - Test states requiring concurrent access
44/// - Background monitoring tasks
45///
46/// # Examples
47///
48/// ```rust
49/// use qubit_function::{ArcTester, Tester};
50/// use std::sync::{Arc, atomic::{AtomicUsize, Ordering}};
51/// use std::thread;
52///
53/// // Shared atomic counter
54/// let counter = Arc::new(AtomicUsize::new(0));
55/// let counter_clone = Arc::clone(&counter);
56///
57/// let shared = ArcTester::new(move || {
58///     counter_clone.load(Ordering::Relaxed) <= 5
59/// });
60///
61/// let clone = shared.clone();
62/// let handle = thread::spawn(move || {
63///     clone.test()
64/// });
65///
66/// assert!(handle.join().expect("thread should not panic"));
67/// counter.fetch_add(1, Ordering::Relaxed);
68/// assert!(shared.test());
69/// ```
70///
71pub struct ArcTester {
72    pub(super) function: Arc<dyn Fn() -> bool + Send + Sync>,
73}
74
75impl ArcTester {
76    /// Creates a new `ArcTester` from a closure
77    ///
78    /// # Type Parameters
79    ///
80    /// * `F` - Closure type implementing `Fn() -> bool + Send + Sync`
81    ///
82    /// # Parameters
83    ///
84    /// * `f` - The closure to wrap
85    ///
86    /// # Return Value
87    ///
88    /// A new `ArcTester` instance
89    ///
90    /// # Examples
91    ///
92    /// ```rust
93    /// use qubit_function::ArcTester;
94    ///
95    /// let tester = ArcTester::new(|| true);
96    /// ```
97    #[inline]
98    pub fn new<F>(f: F) -> Self
99    where
100        F: Fn() -> bool + Send + Sync + 'static,
101    {
102        ArcTester {
103            function: Arc::new(f),
104        }
105    }
106
107    /// Combines this tester with another tester using logical AND
108    ///
109    /// Returns a new `ArcTester` that returns `true` only when both tests
110    /// pass. Borrows `&self`, so the original tester remains available.
111    ///
112    /// # Parameters
113    ///
114    /// * `next` - The tester to combine with
115    ///
116    /// # Return Value
117    ///
118    /// A new `ArcTester` representing logical AND
119    ///
120    /// # Examples
121    ///
122    /// ```rust
123    /// use qubit_function::{ArcTester, Tester};
124    /// use std::sync::{Arc, atomic::{AtomicUsize, AtomicBool, Ordering}};
125    /// use std::thread;
126    ///
127    /// // Simulate database connection pool status
128    /// let active_connections = Arc::new(AtomicUsize::new(0));
129    /// let is_pool_healthy = Arc::new(AtomicBool::new(true));
130    /// let max_connections = 50;
131    ///
132    /// let conn_clone = Arc::clone(&active_connections);
133    /// let health_clone = Arc::clone(&is_pool_healthy);
134    ///
135    /// // Connection pool health check
136    /// let pool_healthy = ArcTester::new(move || {
137    ///     health_clone.load(Ordering::Relaxed)
138    /// });
139    ///
140    /// // Connection count check
141    /// let conn_ok = ArcTester::new(move || {
142    ///     conn_clone.load(Ordering::Relaxed) < max_connections
143    /// });
144    ///
145    /// // Combined check: pool healthy and connection count not exceeded
146    /// let pool_ready = pool_healthy.and(&conn_ok);
147    ///
148    /// // Multi-threaded test
149    /// let pool_ready_clone = pool_ready.clone();
150    /// let handle = thread::spawn(move || {
151    ///     pool_ready_clone.test()
152    /// });
153    ///
154    /// // Initial state should pass
155    /// assert!(handle.join().expect("thread should not panic"));
156    /// assert!(pool_ready.test());
157    ///
158    /// // Connection count exceeded
159    /// active_connections.store(60, Ordering::Relaxed);
160    /// assert!(!pool_ready.test());
161    ///
162    /// // Connection pool unhealthy
163    /// is_pool_healthy.store(false, Ordering::Relaxed);
164    /// assert!(!pool_ready.test());
165    /// ```
166    #[inline]
167    pub fn and(&self, next: &ArcTester) -> ArcTester {
168        let self_fn = Arc::clone(&self.function);
169        let next_fn = Arc::clone(&next.function);
170        ArcTester {
171            function: Arc::new(move || self_fn() && next_fn()),
172        }
173    }
174
175    /// Combines this tester with another tester using logical OR
176    ///
177    /// Returns a new `ArcTester` that returns `true` if either test passes.
178    /// Borrows `&self`, so the original tester remains available.
179    ///
180    /// # Parameters
181    ///
182    /// * `next` - The tester to combine with
183    ///
184    /// # Return Value
185    ///
186    /// A new `ArcTester` representing logical OR
187    ///
188    /// # Examples
189    ///
190    /// ```rust
191    /// use qubit_function::{ArcTester, Tester};
192    /// use std::sync::{Arc, atomic::{AtomicUsize, AtomicBool, Ordering}};
193    /// use std::thread;
194    ///
195    /// // Simulate load balancer status
196    /// let server_load = Arc::new(AtomicUsize::new(0));
197    /// let is_server_healthy = Arc::new(AtomicBool::new(true));
198    /// let max_load = 80;
199    /// let emergency_mode = Arc::new(AtomicBool::new(false));
200    ///
201    /// let load_clone = Arc::clone(&server_load);
202    /// let health_clone = Arc::clone(&is_server_healthy);
203    /// let emergency_clone = Arc::clone(&emergency_mode);
204    ///
205    /// // Server low load
206    /// let low_load = ArcTester::new(move || {
207    ///     load_clone.load(Ordering::Relaxed) < max_load
208    /// });
209    ///
210    /// // Emergency mode check
211    /// let emergency_check = ArcTester::new(move || {
212    ///     emergency_clone.load(Ordering::Relaxed)
213    /// });
214    ///
215    /// // Server health check
216    /// let server_healthy = ArcTester::new(move || {
217    ///     health_clone.load(Ordering::Relaxed)
218    /// });
219    ///
220    /// // Emergency mode or server healthy
221    /// let can_handle_requests = emergency_check.or(&server_healthy);
222    ///
223    /// // Combined condition: low load or can handle requests
224    /// let should_route_here = low_load.or(&can_handle_requests);
225    ///
226    /// // Multi-threaded test
227    /// let router_clone = should_route_here.clone();
228    /// let handle = thread::spawn(move || {
229    ///     router_clone.test()
230    /// });
231    ///
232    /// // Initial state: low load and healthy
233    /// assert!(handle.join().expect("thread should not panic"));
234    /// assert!(should_route_here.test());
235    ///
236    /// // High load but server healthy
237    /// server_load.store(90, Ordering::Relaxed);
238    /// assert!(should_route_here.test()); // still healthy
239    ///
240    /// // Server unhealthy but emergency mode
241    /// is_server_healthy.store(false, Ordering::Relaxed);
242    /// emergency_mode.store(true, Ordering::Relaxed);
243    /// assert!(should_route_here.test()); // emergency mode
244    ///
245    /// // Unhealthy and not emergency mode
246    /// emergency_mode.store(false, Ordering::Relaxed);
247    /// assert!(!should_route_here.test());
248    /// ```
249    #[inline]
250    pub fn or(&self, next: &ArcTester) -> ArcTester {
251        let self_fn = Arc::clone(&self.function);
252        let next_fn = Arc::clone(&next.function);
253        ArcTester {
254            function: Arc::new(move || self_fn() || next_fn()),
255        }
256    }
257
258    /// Combines this tester with another tester using logical NAND
259    ///
260    /// Returns a new `ArcTester` that returns `true` unless both tests pass.
261    /// Borrows `&self`, so the original tester remains available.
262    ///
263    /// # Parameters
264    ///
265    /// * `next` - The tester to combine with
266    ///
267    /// # Return Value
268    ///
269    /// A new `ArcTester` representing logical NAND
270    ///
271    /// # Examples
272    ///
273    /// ```rust
274    /// use qubit_function::{ArcTester, Tester};
275    /// use std::sync::{Arc, atomic::{AtomicBool, Ordering}};
276    /// use std::thread;
277    ///
278    /// let flag1 = Arc::new(AtomicBool::new(true));
279    /// let flag2 = Arc::new(AtomicBool::new(true));
280    ///
281    /// let flag1_clone = Arc::clone(&flag1);
282    /// let flag2_clone = Arc::clone(&flag2);
283    ///
284    /// let tester1 = ArcTester::new(move || {
285    ///     flag1_clone.load(Ordering::Relaxed)
286    /// });
287    ///
288    /// let tester2 = ArcTester::new(move || {
289    ///     flag2_clone.load(Ordering::Relaxed)
290    /// });
291    ///
292    /// let nand = tester1.nand(&tester2);
293    ///
294    /// // Both true returns false
295    /// assert!(!nand.test());
296    ///
297    /// // At least one false returns true
298    /// flag1.store(false, Ordering::Relaxed);
299    /// assert!(nand.test());
300    ///
301    /// // Original tester still available
302    /// assert!(!tester1.test());
303    /// assert!(tester2.test());
304    /// ```
305    #[inline]
306    pub fn nand(&self, next: &ArcTester) -> ArcTester {
307        let self_fn = Arc::clone(&self.function);
308        let next_fn = Arc::clone(&next.function);
309        ArcTester {
310            function: Arc::new(move || !(self_fn() && next_fn())),
311        }
312    }
313
314    /// Combines this tester with another tester using logical XOR
315    ///
316    /// Returns a new `ArcTester` that returns `true` if exactly one test
317    /// passes. Borrows `&self`, so the original tester remains available.
318    ///
319    /// # Parameters
320    ///
321    /// * `next` - The tester to combine with
322    ///
323    /// # Return Value
324    ///
325    /// A new `ArcTester` representing logical XOR
326    ///
327    /// # Examples
328    ///
329    /// ```rust
330    /// use qubit_function::{ArcTester, Tester};
331    /// use std::sync::{Arc, atomic::{AtomicBool, Ordering}};
332    /// use std::thread;
333    ///
334    /// let flag1 = Arc::new(AtomicBool::new(true));
335    /// let flag2 = Arc::new(AtomicBool::new(false));
336    ///
337    /// let flag1_clone = Arc::clone(&flag1);
338    /// let flag2_clone = Arc::clone(&flag2);
339    ///
340    /// let tester1 = ArcTester::new(move || {
341    ///     flag1_clone.load(Ordering::Relaxed)
342    /// });
343    ///
344    /// let tester2 = ArcTester::new(move || {
345    ///     flag2_clone.load(Ordering::Relaxed)
346    /// });
347    ///
348    /// let xor = tester1.xor(&tester2);
349    ///
350    /// // One true one false returns true
351    /// assert!(xor.test());
352    ///
353    /// // Both true returns false
354    /// flag2.store(true, Ordering::Relaxed);
355    /// assert!(!xor.test());
356    ///
357    /// // Both false returns false
358    /// flag1.store(false, Ordering::Relaxed);
359    /// flag2.store(false, Ordering::Relaxed);
360    /// assert!(!xor.test());
361    ///
362    /// // Original tester still available
363    /// assert!(!tester1.test());
364    /// assert!(!tester2.test());
365    /// ```
366    #[inline]
367    pub fn xor(&self, next: &ArcTester) -> ArcTester {
368        let self_fn = Arc::clone(&self.function);
369        let next_fn = Arc::clone(&next.function);
370        ArcTester {
371            function: Arc::new(move || self_fn() ^ next_fn()),
372        }
373    }
374
375    /// Combines this tester with another tester using logical NOR
376    ///
377    /// Returns a new `ArcTester` that returns `true` only when both tests
378    /// fail. Borrows `&self`, so the original tester remains available.
379    ///
380    /// # Parameters
381    ///
382    /// * `next` - The tester to combine with
383    ///
384    /// # Return Value
385    ///
386    /// A new `ArcTester` representing logical NOR
387    ///
388    /// # Examples
389    ///
390    /// ```rust
391    /// use qubit_function::{ArcTester, Tester};
392    /// use std::sync::{Arc, atomic::{AtomicBool, Ordering}};
393    /// use std::thread;
394    ///
395    /// let flag1 = Arc::new(AtomicBool::new(false));
396    /// let flag2 = Arc::new(AtomicBool::new(false));
397    ///
398    /// let flag1_clone = Arc::clone(&flag1);
399    /// let flag2_clone = Arc::clone(&flag2);
400    ///
401    /// let tester1 = ArcTester::new(move || {
402    ///     flag1_clone.load(Ordering::Relaxed)
403    /// });
404    ///
405    /// let tester2 = ArcTester::new(move || {
406    ///     flag2_clone.load(Ordering::Relaxed)
407    /// });
408    ///
409    /// let nor = tester1.nor(&tester2);
410    ///
411    /// // Both false returns true
412    /// assert!(nor.test());
413    ///
414    /// // At least one true returns false
415    /// flag1.store(true, Ordering::Relaxed);
416    /// assert!(!nor.test());
417    ///
418    /// // Original tester still available
419    /// assert!(tester1.test());
420    /// assert!(!tester2.test());
421    /// ```
422    #[inline]
423    pub fn nor(&self, next: &ArcTester) -> ArcTester {
424        let self_fn = Arc::clone(&self.function);
425        let next_fn = Arc::clone(&next.function);
426        ArcTester {
427            function: Arc::new(move || !(self_fn() || next_fn())),
428        }
429    }
430}
431
432impl Not for ArcTester {
433    type Output = ArcTester;
434
435    #[inline]
436    fn not(self) -> Self::Output {
437        let func = self.function;
438        ArcTester {
439            function: Arc::new(move || !func()),
440        }
441    }
442}
443
444impl Not for &ArcTester {
445    type Output = ArcTester;
446
447    #[inline]
448    fn not(self) -> Self::Output {
449        let func = Arc::clone(&self.function);
450        ArcTester {
451            function: Arc::new(move || !func()),
452        }
453    }
454}
455
456impl Tester for ArcTester {
457    #[inline]
458    fn test(&self) -> bool {
459        (self.function)()
460    }
461
462    #[inline]
463    fn into_box(self) -> BoxTester {
464        let func = self.function;
465        BoxTester {
466            function: Box::new(move || func()),
467        }
468    }
469
470    #[inline]
471    fn into_rc(self) -> RcTester {
472        let func = self.function;
473        RcTester {
474            function: Rc::new(move || func()),
475        }
476    }
477
478    #[inline]
479    fn into_arc(self) -> ArcTester {
480        self
481    }
482
483    #[inline]
484    fn into_fn(self) -> impl Fn() -> bool {
485        move || (self.function)()
486    }
487
488    #[inline]
489    fn to_box(&self) -> BoxTester {
490        let self_fn = self.function.clone();
491        BoxTester {
492            function: Box::new(move || self_fn()),
493        }
494    }
495
496    #[inline]
497    fn to_rc(&self) -> RcTester {
498        let self_fn = self.function.clone();
499        RcTester {
500            function: Rc::new(move || self_fn()),
501        }
502    }
503
504    #[inline]
505    fn to_arc(&self) -> ArcTester {
506        self.clone()
507    }
508
509    #[inline]
510    fn to_fn(&self) -> impl Fn() -> bool {
511        let self_fn = self.function.clone();
512        move || self_fn()
513    }
514}
515
516impl Clone for ArcTester {
517    /// Creates a clone of this `ArcTester`.
518    ///
519    /// The cloned instance shares the same underlying function with
520    /// the original, allowing multiple references to the same test
521    /// logic.
522    #[inline]
523    fn clone(&self) -> Self {
524        Self {
525            function: Arc::clone(&self.function),
526        }
527    }
528}