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