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}