Skip to main content

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