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