Skip to main content

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