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}