qubit_function/tester.rs
1/*******************************************************************************
2 *
3 * Copyright (c) 2025 - 2026.
4 * Haixing Hu, Qubit Co. Ltd.
5 *
6 * All rights reserved.
7 *
8 ******************************************************************************/
9//! # Tester Type
10//!
11//! Provides tester implementations that test conditions or states and return
12//! boolean values, without accepting input parameters.
13//!
14//! # Overview
15//!
16//! **Tester** is a functional abstraction for testing conditions or states
17//! without accepting input. It can check system status, wait for conditions,
18//! or perform health checks.
19//!
20//! This module implements **Option 3** from the design document: a unified
21//! `Tester` trait with multiple concrete implementations optimized for
22//! different ownership and concurrency scenarios.
23//!
24//! # Core Design Principles
25//!
26//! 1. **Returns boolean**: `Tester` returns `bool` to indicate test results
27//! 2. **Uses `&self`**: Tester is only responsible for "judgment", not
28//! "state management"
29//! 3. **No TesterOnce**: Very limited use cases, lacks practical examples
30//! 4. **State management is caller's responsibility**: Tester only reads
31//! state, does not modify state
32//!
33//! # Three Implementations
34//!
35//! - **`BoxTester`**: Single ownership using `Box<dyn Fn() -> bool>`.
36//! Zero overhead, cannot be cloned. Best for one-time use and builder
37//! patterns.
38//!
39//! - **`ArcTester`**: Thread-safe shared ownership using
40//! `Arc<dyn Fn() -> bool + Send + Sync>`. Can be cloned and sent across
41//! threads. Lock-free overhead.
42//!
43//! - **`RcTester`**: Single-threaded shared ownership using
44//! `Rc<dyn Fn() -> bool>`. Can be cloned but cannot be sent across
45//! threads. Lower overhead than `ArcTester`.
46//!
47//! # Comparison with Other Functional Abstractions
48//!
49//! | Type | Input | Output | self | Modify? | Use Cases |
50//! |-----------|-------|--------|-----------|---------|-------------|
51//! | Tester | None | `bool` | `&self` | No | State Check |
52//! | Predicate | `&T` | `bool` | `&self` | No | Filter |
53//! | Supplier | None | `T` | `&mut` | Yes | Factory |
54//!
55//! # Examples
56//!
57//! ## Basic State Checking
58//!
59//! ```rust
60//! use qubit_function::{BoxTester, Tester};
61//! use std::sync::{Arc, atomic::{AtomicUsize, Ordering}};
62//!
63//! // State managed externally
64//! let count = Arc::new(AtomicUsize::new(0));
65//! let count_clone = Arc::clone(&count);
66//!
67//! let tester = BoxTester::new(move || {
68//! count_clone.load(Ordering::Relaxed) <= 3
69//! });
70//!
71//! assert!(tester.test()); // true (0)
72//! count.fetch_add(1, Ordering::Relaxed);
73//! assert!(tester.test()); // true (1)
74//! count.fetch_add(1, Ordering::Relaxed);
75//! assert!(tester.test()); // true (2)
76//! count.fetch_add(1, Ordering::Relaxed);
77//! assert!(tester.test()); // true (3)
78//! count.fetch_add(1, Ordering::Relaxed);
79//! assert!(!tester.test()); // false (4)
80//! ```
81//!
82//! ## Logical Combination
83//!
84//! ```rust
85//! use qubit_function::{BoxTester, Tester};
86//! use std::sync::{Arc, atomic::{AtomicUsize, AtomicBool, Ordering}};
87//!
88//! // Simulate microservice health check scenario
89//! let cpu_usage = Arc::new(AtomicUsize::new(0));
90//! let memory_usage = Arc::new(AtomicUsize::new(0));
91//! let is_healthy = Arc::new(AtomicBool::new(true));
92//! let is_ready = Arc::new(AtomicBool::new(false));
93//! let max_cpu = 80;
94//! let max_memory = 90;
95//!
96//! let cpu_clone = Arc::clone(&cpu_usage);
97//! let memory_clone = Arc::clone(&memory_usage);
98//! let health_clone = Arc::clone(&is_healthy);
99//! let ready_clone = Arc::clone(&is_ready);
100//!
101//! // System resource check: CPU and memory within safe limits
102//! let resources_ok = BoxTester::new(move || {
103//! cpu_clone.load(Ordering::Relaxed) < max_cpu
104//! })
105//! .and(move || {
106//! memory_clone.load(Ordering::Relaxed) < max_memory
107//! });
108//!
109//! // Service status check: healthy or ready
110//! let service_ok = BoxTester::new(move || {
111//! health_clone.load(Ordering::Relaxed)
112//! })
113//! .or(move || {
114//! ready_clone.load(Ordering::Relaxed)
115//! });
116//!
117//! // Combined condition: resources normal and service available
118//! let can_accept_traffic = resources_ok.and(service_ok);
119//!
120//! // Test different state combinations
121//! // Initial state: resources normal and service healthy
122//! cpu_usage.store(50, Ordering::Relaxed);
123//! memory_usage.store(60, Ordering::Relaxed);
124//! assert!(can_accept_traffic.test()); // resources normal and service healthy
125//!
126//! // Service unhealthy but ready
127//! is_healthy.store(false, Ordering::Relaxed);
128//! is_ready.store(true, Ordering::Relaxed);
129//! assert!(can_accept_traffic.test()); // resources normal and service ready
130//!
131//! // CPU usage too high
132//! cpu_usage.store(95, Ordering::Relaxed);
133//! assert!(!can_accept_traffic.test()); // resources exceeded
134//!
135//! // Service unhealthy but ready
136//! is_healthy.store(false, Ordering::Relaxed);
137//! cpu_usage.store(50, Ordering::Relaxed);
138//! assert!(can_accept_traffic.test()); // still ready
139//! ```
140//!
141//! ## Thread-Safe Sharing
142//!
143//! ```rust
144//! use qubit_function::{ArcTester, Tester};
145//! use std::thread;
146//!
147//! let shared = ArcTester::new(|| true);
148//! let clone = shared.clone();
149//!
150//! let handle = thread::spawn(move || {
151//! clone.test()
152//! });
153//!
154//! assert!(handle.join().unwrap());
155//! ```
156//!
157//! # Author
158//!
159//! Haixing Hu
160use std::rc::Rc;
161use std::sync::Arc;
162
163// ============================================================================
164// Core Tester Trait
165// ============================================================================
166
167/// Tests whether a state or condition holds
168///
169/// Tester is a functional abstraction for testing states or conditions. It
170/// accepts no parameters and returns a boolean value indicating the test
171/// result of some state or condition.
172///
173/// # Core Characteristics
174///
175/// - **No input parameters**: Captures context through closures
176/// - **Returns boolean**: Indicates test results
177/// - **Uses `&self`**: Does not modify its own state, only reads external
178/// state
179/// - **Repeatable calls**: The same Tester can call `test()` multiple times
180///
181/// # Use Cases
182///
183/// - **State checking**: Check system or service status
184/// - **Condition waiting**: Repeatedly check until conditions are met
185/// - **Health monitoring**: Check system health status
186/// - **Precondition validation**: Verify conditions before operations
187///
188/// # Design Philosophy
189///
190/// Tester's responsibility is "test judgment", not "state management".
191/// State management is the caller's responsibility. Tester only reads state
192/// and returns judgment results.
193///
194/// # Examples
195///
196/// ```rust
197/// use qubit_function::{BoxTester, Tester};
198/// use std::sync::{Arc, atomic::{AtomicBool, Ordering}};
199///
200/// // State managed externally
201/// let ready = Arc::new(AtomicBool::new(false));
202/// let ready_clone = Arc::clone(&ready);
203///
204/// // Tester only responsible for reading state
205/// let tester = BoxTester::new(move || {
206/// ready_clone.load(Ordering::Acquire)
207/// });
208///
209/// // Can be called multiple times
210/// assert!(!tester.test());
211/// ready.store(true, Ordering::Release);
212/// assert!(tester.test());
213/// ```
214///
215/// # Author
216///
217/// Haixing Hu
218pub trait Tester {
219 /// Executes the test and returns the test result
220 ///
221 /// This method can be called multiple times without modifying the Tester's
222 /// own state.
223 ///
224 /// # Return Value
225 ///
226 /// Returns `true` if the condition holds, otherwise returns `false`
227 ///
228 /// # Examples
229 ///
230 /// ```rust
231 /// use qubit_function::{BoxTester, Tester};
232 ///
233 /// let tester = BoxTester::new(|| true);
234 /// assert!(tester.test());
235 /// ```
236 fn test(&self) -> bool;
237
238 /// Converts this tester to `BoxTester`
239 ///
240 /// # Return Value
241 ///
242 /// A `BoxTester` that wraps this tester
243 ///
244 /// # Examples
245 ///
246 /// ```rust
247 /// use qubit_function::{Tester, BoxTester};
248 ///
249 /// let closure = || true;
250 /// let boxed: BoxTester = closure.into_box();
251 /// ```
252 #[inline]
253 fn into_box(self) -> BoxTester
254 where
255 Self: Sized + 'static,
256 {
257 BoxTester {
258 function: Box::new(move || self.test()),
259 }
260 }
261
262 /// Converts this tester to `RcTester`
263 ///
264 /// # Return Value
265 ///
266 /// A `RcTester` that wraps this tester
267 ///
268 /// # Examples
269 ///
270 /// ```rust
271 /// use qubit_function::{Tester, RcTester};
272 ///
273 /// let closure = || true;
274 /// let rc: RcTester = closure.into_rc();
275 /// ```
276 #[inline]
277 fn into_rc(self) -> RcTester
278 where
279 Self: Sized + 'static,
280 {
281 RcTester {
282 function: Rc::new(move || self.test()),
283 }
284 }
285
286 /// Converts this tester to `ArcTester`
287 ///
288 /// # Return Value
289 ///
290 /// An `ArcTester` that wraps this tester
291 ///
292 /// # Examples
293 ///
294 /// ```rust
295 /// use qubit_function::{Tester, ArcTester};
296 ///
297 /// let closure = || true;
298 /// let arc: ArcTester = closure.into_arc();
299 /// ```
300 #[inline]
301 fn into_arc(self) -> ArcTester
302 where
303 Self: Sized + Send + Sync + 'static,
304 {
305 ArcTester {
306 function: Arc::new(move || self.test()),
307 }
308 }
309
310 /// Converts this tester to a boxed function pointer
311 ///
312 /// # Return Value
313 ///
314 /// A `Box<dyn Fn() -> bool>` that wraps this tester
315 ///
316 /// # Examples
317 ///
318 /// ```rust
319 /// use qubit_function::Tester;
320 ///
321 /// let closure = || true;
322 /// let func = closure.into_fn();
323 /// assert!(func());
324 /// ```
325 #[inline]
326 fn into_fn(self) -> impl Fn() -> bool
327 where
328 Self: Sized + 'static,
329 {
330 Box::new(move || self.test())
331 }
332
333 /// Clones and converts this tester to `BoxTester`
334 ///
335 /// # Return Value
336 ///
337 /// A `BoxTester` that wraps a clone of this tester
338 ///
339 /// # Examples
340 ///
341 /// ```rust
342 /// use qubit_function::{Tester, BoxTester, ArcTester};
343 ///
344 /// let arc = ArcTester::new(|| true);
345 /// let boxed: BoxTester = arc.to_box();
346 /// // arc is still available
347 /// ```
348 #[inline]
349 fn to_box(&self) -> BoxTester
350 where
351 Self: Clone + 'static,
352 {
353 self.clone().into_box()
354 }
355
356 /// Clones and converts this tester to `RcTester`
357 ///
358 /// # Return Value
359 ///
360 /// A `RcTester` that wraps a clone of this tester
361 ///
362 /// # Examples
363 ///
364 /// ```rust
365 /// use qubit_function::{Tester, RcTester, ArcTester};
366 ///
367 /// let arc = ArcTester::new(|| true);
368 /// let rc: RcTester = arc.to_rc();
369 /// // arc is still available
370 /// ```
371 #[inline]
372 fn to_rc(&self) -> RcTester
373 where
374 Self: Clone + 'static,
375 {
376 self.clone().into_rc()
377 }
378
379 /// Clones and converts this tester to `ArcTester`
380 ///
381 /// # Return Value
382 ///
383 /// An `ArcTester` that wraps a clone of this tester
384 ///
385 /// # Examples
386 ///
387 /// ```rust
388 /// use qubit_function::{Tester, ArcTester, RcTester};
389 ///
390 /// let rc = RcTester::new(|| true);
391 /// // Note: This will panic for RcTester as it's not Send + Sync
392 /// // let arc: ArcTester = rc.to_arc();
393 /// ```
394 #[inline]
395 fn to_arc(&self) -> ArcTester
396 where
397 Self: Clone + Send + Sync + 'static,
398 {
399 self.clone().into_arc()
400 }
401
402 /// Clones and converts this tester to a boxed function pointer
403 ///
404 /// # Return Value
405 ///
406 /// A `Box<dyn Fn() -> bool>` that wraps a clone of this tester
407 ///
408 /// # Examples
409 ///
410 /// ```rust
411 /// use qubit_function::{Tester, ArcTester};
412 ///
413 /// let arc = ArcTester::new(|| true);
414 /// let func = arc.to_fn();
415 /// // arc is still available
416 /// assert!(func());
417 /// ```
418 #[inline]
419 fn to_fn(&self) -> impl Fn() -> bool
420 where
421 Self: Clone + 'static,
422 {
423 self.clone().into_fn()
424 }
425}
426
427// ============================================================================
428// BoxTester: Single Ownership Implementation
429// ============================================================================
430
431/// Single ownership Tester implemented using `Box`
432///
433/// `BoxTester` wraps a closure in `Box<dyn Fn() -> bool>`, providing single
434/// ownership semantics with no additional allocation overhead beyond the
435/// initial boxing.
436///
437/// # Characteristics
438///
439/// - **Single ownership**: Cannot be cloned
440/// - **Zero overhead**: Single heap allocation
441/// - **Consuming combination**: `and()`/`or()`/`not()` consume `self`
442/// - **Type flexibility**: Accepts any `Tester` implementation
443///
444/// # Use Cases
445///
446/// - One-time testing scenarios
447/// - Builder patterns requiring ownership transfer
448/// - Simple state checking without sharing
449/// - Chained calls with ownership transfer
450///
451/// # Examples
452///
453/// ```rust
454/// use qubit_function::{BoxTester, Tester};
455/// use std::sync::{Arc, atomic::{AtomicUsize, Ordering}};
456///
457/// // State managed externally
458/// let count = Arc::new(AtomicUsize::new(0));
459/// let count_clone = Arc::clone(&count);
460///
461/// let tester = BoxTester::new(move || {
462/// count_clone.load(Ordering::Relaxed) < 3
463/// });
464///
465/// assert!(tester.test());
466/// count.fetch_add(1, Ordering::Relaxed);
467/// assert!(tester.test());
468/// count.fetch_add(1, Ordering::Relaxed);
469/// assert!(tester.test());
470/// count.fetch_add(1, Ordering::Relaxed);
471/// assert!(!tester.test());
472///
473/// // Logical combination
474/// let combined = BoxTester::new(|| true)
475/// .and(|| false)
476/// .or(|| true);
477/// assert!(combined.test());
478/// ```
479///
480/// # Author
481///
482/// Haixing Hu
483pub struct BoxTester {
484 function: Box<dyn Fn() -> bool>,
485}
486
487impl BoxTester {
488 /// Creates a new `BoxTester` from a closure
489 ///
490 /// # Type Parameters
491 ///
492 /// * `F` - Closure type implementing `Fn() -> bool`
493 ///
494 /// # Parameters
495 ///
496 /// * `f` - The closure to wrap
497 ///
498 /// # Return Value
499 ///
500 /// A new `BoxTester` instance
501 ///
502 /// # Examples
503 ///
504 /// ```rust
505 /// use qubit_function::BoxTester;
506 ///
507 /// let tester = BoxTester::new(|| true);
508 /// ```
509 #[inline]
510 pub fn new<F>(f: F) -> Self
511 where
512 F: Fn() -> bool + 'static,
513 {
514 BoxTester {
515 function: Box::new(f),
516 }
517 }
518
519 /// Combines this tester with another tester using logical AND
520 ///
521 /// Returns a new `BoxTester` that returns `true` only when both tests
522 /// pass. Short-circuit evaluation: if the first test fails, the second
523 /// will not be executed.
524 ///
525 /// # Type Parameters
526 ///
527 /// * `T` - Type implementing `Tester`
528 ///
529 /// # Parameters
530 ///
531 /// * `next` - The tester to combine with
532 ///
533 /// # Return Value
534 ///
535 /// A new `BoxTester` representing logical AND
536 ///
537 /// # Examples
538 ///
539 /// ```rust
540 /// use qubit_function::{BoxTester, Tester};
541 /// use std::sync::{Arc, atomic::{AtomicUsize, AtomicBool, Ordering}};
542 ///
543 /// // Simulate service status
544 /// let request_count = Arc::new(AtomicUsize::new(0));
545 /// let is_available = Arc::new(AtomicBool::new(true));
546 /// let max_requests = 1000;
547 ///
548 /// let count_clone = Arc::clone(&request_count);
549 /// let available_clone = Arc::clone(&is_available);
550 ///
551 /// // Service available and request count not exceeded
552 /// let service_ok = BoxTester::new(move || {
553 /// available_clone.load(Ordering::Relaxed)
554 /// })
555 /// .and(move || {
556 /// count_clone.load(Ordering::Relaxed) < max_requests
557 /// });
558 ///
559 /// // Initial state: available and request count 0
560 /// assert!(service_ok.test());
561 ///
562 /// // Simulate request increase
563 /// request_count.store(500, Ordering::Relaxed);
564 /// assert!(service_ok.test());
565 ///
566 /// // Request count exceeded
567 /// request_count.store(1500, Ordering::Relaxed);
568 /// assert!(!service_ok.test());
569 ///
570 /// // Service unavailable
571 /// is_available.store(false, Ordering::Relaxed);
572 /// assert!(!service_ok.test());
573 /// ```
574 #[inline]
575 pub fn and<T>(self, next: T) -> BoxTester
576 where
577 T: Tester + 'static,
578 {
579 let self_fn = self.function;
580 let next_tester = next;
581 BoxTester::new(move || self_fn() && next_tester.test())
582 }
583
584 /// Combines this tester with another tester using logical OR
585 ///
586 /// Returns a new `BoxTester` that returns `true` if either test passes.
587 /// Short-circuit evaluation: if the first test passes, the second will
588 /// not be executed.
589 ///
590 /// # Type Parameters
591 ///
592 /// * `T` - Type implementing `Tester`
593 ///
594 /// # Parameters
595 ///
596 /// * `next` - The tester to combine with
597 ///
598 /// # Return Value
599 ///
600 /// A new `BoxTester` representing logical OR
601 ///
602 /// # Examples
603 ///
604 /// ```rust
605 /// use qubit_function::{BoxTester, Tester};
606 /// use std::sync::{Arc, atomic::{AtomicUsize, AtomicBool, Ordering}};
607 ///
608 /// // Simulate service status
609 /// let request_count = Arc::new(AtomicUsize::new(0));
610 /// let is_healthy = Arc::new(AtomicBool::new(true));
611 /// let max_requests = 100;
612 ///
613 /// let count_clone = Arc::clone(&request_count);
614 /// let health_clone = Arc::clone(&is_healthy);
615 ///
616 /// // Service healthy or low request count
617 /// let can_serve = BoxTester::new(move || {
618 /// health_clone.load(Ordering::Relaxed)
619 /// })
620 /// .or(move || {
621 /// count_clone.load(Ordering::Relaxed) < max_requests
622 /// });
623 ///
624 /// // Initial state: healthy and request count 0
625 /// assert!(can_serve.test());
626 ///
627 /// // Request count increased but within limit
628 /// request_count.store(50, Ordering::Relaxed);
629 /// assert!(can_serve.test());
630 ///
631 /// // Request count exceeded but service healthy
632 /// request_count.store(150, Ordering::Relaxed);
633 /// assert!(can_serve.test()); // still healthy
634 ///
635 /// // Service unhealthy but low request count
636 /// is_healthy.store(false, Ordering::Relaxed);
637 /// request_count.store(50, Ordering::Relaxed);
638 /// assert!(can_serve.test()); // low request count
639 ///
640 /// // Unhealthy and high request count
641 /// request_count.store(150, Ordering::Relaxed);
642 /// assert!(!can_serve.test());
643 /// ```
644 #[inline]
645 pub fn or<T>(self, next: T) -> BoxTester
646 where
647 T: Tester + 'static,
648 {
649 let self_fn = self.function;
650 let next_tester = next;
651 BoxTester::new(move || self_fn() || next_tester.test())
652 }
653
654 /// Negates the result of this tester
655 ///
656 /// Returns a new `BoxTester` that returns the opposite value of the
657 /// original test result.
658 ///
659 /// # Return Value
660 ///
661 /// A new `BoxTester` representing logical NOT
662 ///
663 /// # Examples
664 ///
665 /// ```rust
666 /// use qubit_function::{BoxTester, Tester};
667 /// use std::sync::{Arc, atomic::{AtomicUsize, Ordering}};
668 ///
669 /// // Simulate resource usage
670 /// let memory_usage = Arc::new(AtomicUsize::new(0));
671 /// let max_memory = 1024; // MB
672 ///
673 /// let memory_clone = Arc::clone(&memory_usage);
674 ///
675 /// // Memory usage not exceeded
676 /// let memory_ok = BoxTester::new(move || {
677 /// memory_clone.load(Ordering::Relaxed) <= max_memory
678 /// });
679 ///
680 /// // Initial state: normal memory usage
681 /// memory_usage.store(512, Ordering::Relaxed);
682 /// assert!(memory_ok.test());
683 ///
684 /// // Memory usage exceeded (negated)
685 /// let memory_critical = memory_ok.not();
686 /// assert!(!memory_critical.test());
687 ///
688 /// // Memory usage exceeded
689 /// memory_usage.store(2048, Ordering::Relaxed);
690 /// assert!(memory_critical.test());
691 /// ```
692 #[allow(clippy::should_implement_trait)]
693 #[inline]
694 pub fn not(self) -> BoxTester {
695 let self_fn = self.function;
696 BoxTester::new(move || !self_fn())
697 }
698
699 /// Combines this tester with another tester using logical NAND
700 ///
701 /// Returns a new `BoxTester` that returns `true` unless both tests pass.
702 /// Equivalent to `!(self AND other)`.
703 ///
704 /// # Type Parameters
705 ///
706 /// * `T` - Type implementing `Tester`
707 ///
708 /// # Parameters
709 ///
710 /// * `next` - The tester to combine with
711 ///
712 /// # Return Value
713 ///
714 /// A new `BoxTester` representing logical NAND
715 ///
716 /// # Examples
717 ///
718 /// ```rust
719 /// use qubit_function::{BoxTester, Tester};
720 /// use std::sync::{Arc, atomic::{AtomicBool, Ordering}};
721 ///
722 /// let flag1 = Arc::new(AtomicBool::new(true));
723 /// let flag2 = Arc::new(AtomicBool::new(true));
724 ///
725 /// let flag1_clone = Arc::clone(&flag1);
726 /// let flag2_clone = Arc::clone(&flag2);
727 ///
728 /// let nand = BoxTester::new(move || {
729 /// flag1_clone.load(Ordering::Relaxed)
730 /// })
731 /// .nand(move || {
732 /// flag2_clone.load(Ordering::Relaxed)
733 /// });
734 ///
735 /// // Both true returns false
736 /// assert!(!nand.test());
737 ///
738 /// // At least one false returns true
739 /// flag1.store(false, Ordering::Relaxed);
740 /// assert!(nand.test());
741 /// ```
742 #[inline]
743 pub fn nand<T>(self, next: T) -> BoxTester
744 where
745 T: Tester + 'static,
746 {
747 let self_fn = self.function;
748 let next_tester = next;
749 BoxTester::new(move || !(self_fn() && next_tester.test()))
750 }
751
752 /// Combines this tester with another tester using logical XOR
753 ///
754 /// Returns a new `BoxTester` that returns `true` if exactly one test
755 /// passes.
756 ///
757 /// # Type Parameters
758 ///
759 /// * `T` - Type implementing `Tester`
760 ///
761 /// # Parameters
762 ///
763 /// * `next` - The tester to combine with
764 ///
765 /// # Return Value
766 ///
767 /// A new `BoxTester` representing logical XOR
768 ///
769 /// # Examples
770 ///
771 /// ```rust
772 /// use qubit_function::{BoxTester, Tester};
773 /// use std::sync::{Arc, atomic::{AtomicBool, Ordering}};
774 ///
775 /// let flag1 = Arc::new(AtomicBool::new(true));
776 /// let flag2 = Arc::new(AtomicBool::new(false));
777 ///
778 /// let flag1_clone1 = Arc::clone(&flag1);
779 /// let flag2_clone1 = Arc::clone(&flag2);
780 ///
781 /// let xor = BoxTester::new(move || {
782 /// flag1_clone1.load(Ordering::Relaxed)
783 /// })
784 /// .xor(move || {
785 /// flag2_clone1.load(Ordering::Relaxed)
786 /// });
787 ///
788 /// // One true one false returns true
789 /// assert!(xor.test());
790 ///
791 /// // Both true returns false
792 /// flag2.store(true, Ordering::Relaxed);
793 /// assert!(!xor.test());
794 ///
795 /// // Both false returns false
796 /// flag1.store(false, Ordering::Relaxed);
797 /// flag2.store(false, Ordering::Relaxed);
798 /// assert!(!xor.test());
799 /// ```
800 #[inline]
801 pub fn xor<T>(self, next: T) -> BoxTester
802 where
803 T: Tester + 'static,
804 {
805 let self_fn = self.function;
806 let next_tester = next;
807 BoxTester::new(move || self_fn() ^ next_tester.test())
808 }
809
810 /// Combines this tester with another tester using logical NOR
811 ///
812 /// Returns a new `BoxTester` that returns `true` only when both tests
813 /// fail. Equivalent to `!(self OR other)`.
814 ///
815 /// # Type Parameters
816 ///
817 /// * `T` - Type implementing `Tester`
818 ///
819 /// # Parameters
820 ///
821 /// * `next` - The tester to combine with
822 ///
823 /// # Return Value
824 ///
825 /// A new `BoxTester` representing logical NOR
826 ///
827 /// # Examples
828 ///
829 /// ```rust
830 /// use qubit_function::{BoxTester, Tester};
831 /// use std::sync::{Arc, atomic::{AtomicBool, Ordering}};
832 ///
833 /// let flag1 = Arc::new(AtomicBool::new(false));
834 /// let flag2 = Arc::new(AtomicBool::new(false));
835 ///
836 /// let flag1_clone = Arc::clone(&flag1);
837 /// let flag2_clone = Arc::clone(&flag2);
838 ///
839 /// let nor = BoxTester::new(move || {
840 /// flag1_clone.load(Ordering::Relaxed)
841 /// })
842 /// .nor(move || {
843 /// flag2_clone.load(Ordering::Relaxed)
844 /// });
845 ///
846 /// // Both false returns true
847 /// assert!(nor.test());
848 ///
849 /// // At least one true returns false
850 /// flag1.store(true, Ordering::Relaxed);
851 /// assert!(!nor.test());
852 /// ```
853 #[inline]
854 pub fn nor<T>(self, next: T) -> BoxTester
855 where
856 T: Tester + 'static,
857 {
858 let self_fn = self.function;
859 let next_tester = next;
860 BoxTester::new(move || !(self_fn() || next_tester.test()))
861 }
862}
863
864impl Tester for BoxTester {
865 #[inline]
866 fn test(&self) -> bool {
867 (self.function)()
868 }
869
870 #[inline]
871 fn into_box(self) -> BoxTester {
872 self
873 }
874
875 #[inline]
876 fn into_rc(self) -> RcTester {
877 let func = self.function;
878 RcTester {
879 function: Rc::new(func),
880 }
881 }
882
883 // Note: BoxTester does not implement Send + Sync, so into_arc()
884 // cannot be implemented. Calling into_arc() on BoxTester will result
885 // in a compile error due to the Send + Sync trait bounds not being
886 // satisfied. The default Tester trait implementation will be used.
887
888 #[inline]
889 fn into_fn(self) -> impl Fn() -> bool {
890 self.function
891 }
892
893 // Note: BoxTester does not implement Clone, so to_box(), to_rc(),
894 // to_arc(), and to_fn() cannot be implemented. Calling these methods
895 // on BoxTester will result in a compile error due to the Clone trait
896 // bound not being satisfied. The default Tester trait implementations
897 // will be used.
898}
899
900// ============================================================================
901// ArcTester: Thread-Safe Shared Ownership Implementation
902// ============================================================================
903
904/// Thread-safe shared ownership Tester implemented using `Arc`
905///
906/// `ArcTester` wraps a closure in `Arc<dyn Fn() -> bool + Send + Sync>`,
907/// allowing the tester to be cloned and safely shared across threads.
908///
909/// # Characteristics
910///
911/// - **Shared ownership**: Can be cloned
912/// - **Thread-safe**: Can be sent across threads
913/// - **Lock-free overhead**: Uses `Fn` without needing `Mutex`
914/// - **Borrowing combination**: `and()`/`or()`/`not()` borrow `&self`
915///
916/// # Use Cases
917///
918/// - Multi-threaded testing scenarios
919/// - Health checks shared across threads
920/// - Test states requiring concurrent access
921/// - Background monitoring tasks
922///
923/// # Examples
924///
925/// ```rust
926/// use qubit_function::{ArcTester, Tester};
927/// use std::sync::{Arc, atomic::{AtomicUsize, Ordering}};
928/// use std::thread;
929///
930/// // Shared atomic counter
931/// let counter = Arc::new(AtomicUsize::new(0));
932/// let counter_clone = Arc::clone(&counter);
933///
934/// let shared = ArcTester::new(move || {
935/// counter_clone.load(Ordering::Relaxed) <= 5
936/// });
937///
938/// let clone = shared.clone();
939/// let handle = thread::spawn(move || {
940/// clone.test()
941/// });
942///
943/// assert!(handle.join().unwrap());
944/// counter.fetch_add(1, Ordering::Relaxed);
945/// assert!(shared.test());
946/// ```
947///
948/// # Author
949///
950/// Haixing Hu
951pub struct ArcTester {
952 function: Arc<dyn Fn() -> bool + Send + Sync>,
953}
954
955impl ArcTester {
956 /// Creates a new `ArcTester` from a closure
957 ///
958 /// # Type Parameters
959 ///
960 /// * `F` - Closure type implementing `Fn() -> bool + Send + Sync`
961 ///
962 /// # Parameters
963 ///
964 /// * `f` - The closure to wrap
965 ///
966 /// # Return Value
967 ///
968 /// A new `ArcTester` instance
969 ///
970 /// # Examples
971 ///
972 /// ```rust
973 /// use qubit_function::ArcTester;
974 ///
975 /// let tester = ArcTester::new(|| true);
976 /// ```
977 #[inline]
978 pub fn new<F>(f: F) -> Self
979 where
980 F: Fn() -> bool + Send + Sync + 'static,
981 {
982 ArcTester {
983 function: Arc::new(f),
984 }
985 }
986
987 /// Combines this tester with another tester using logical AND
988 ///
989 /// Returns a new `ArcTester` that returns `true` only when both tests
990 /// pass. Borrows `&self`, so the original tester remains available.
991 ///
992 /// # Parameters
993 ///
994 /// * `next` - The tester to combine with
995 ///
996 /// # Return Value
997 ///
998 /// A new `ArcTester` representing logical AND
999 ///
1000 /// # Examples
1001 ///
1002 /// ```rust
1003 /// use qubit_function::{ArcTester, Tester};
1004 /// use std::sync::{Arc, atomic::{AtomicUsize, AtomicBool, Ordering}};
1005 /// use std::thread;
1006 ///
1007 /// // Simulate database connection pool status
1008 /// let active_connections = Arc::new(AtomicUsize::new(0));
1009 /// let is_pool_healthy = Arc::new(AtomicBool::new(true));
1010 /// let max_connections = 50;
1011 ///
1012 /// let conn_clone = Arc::clone(&active_connections);
1013 /// let health_clone = Arc::clone(&is_pool_healthy);
1014 ///
1015 /// // Connection pool health check
1016 /// let pool_healthy = ArcTester::new(move || {
1017 /// health_clone.load(Ordering::Relaxed)
1018 /// });
1019 ///
1020 /// // Connection count check
1021 /// let conn_ok = ArcTester::new(move || {
1022 /// conn_clone.load(Ordering::Relaxed) < max_connections
1023 /// });
1024 ///
1025 /// // Combined check: pool healthy and connection count not exceeded
1026 /// let pool_ready = pool_healthy.and(&conn_ok);
1027 ///
1028 /// // Multi-threaded test
1029 /// let pool_ready_clone = pool_ready.clone();
1030 /// let handle = thread::spawn(move || {
1031 /// pool_ready_clone.test()
1032 /// });
1033 ///
1034 /// // Initial state should pass
1035 /// assert!(handle.join().unwrap());
1036 /// assert!(pool_ready.test());
1037 ///
1038 /// // Connection count exceeded
1039 /// active_connections.store(60, Ordering::Relaxed);
1040 /// assert!(!pool_ready.test());
1041 ///
1042 /// // Connection pool unhealthy
1043 /// is_pool_healthy.store(false, Ordering::Relaxed);
1044 /// assert!(!pool_ready.test());
1045 /// ```
1046 #[inline]
1047 pub fn and(&self, next: &ArcTester) -> ArcTester {
1048 let self_fn = Arc::clone(&self.function);
1049 let next_fn = Arc::clone(&next.function);
1050 ArcTester {
1051 function: Arc::new(move || self_fn() && next_fn()),
1052 }
1053 }
1054
1055 /// Combines this tester with another tester using logical OR
1056 ///
1057 /// Returns a new `ArcTester` that returns `true` if either test passes.
1058 /// Borrows `&self`, so the original tester remains available.
1059 ///
1060 /// # Parameters
1061 ///
1062 /// * `next` - The tester to combine with
1063 ///
1064 /// # Return Value
1065 ///
1066 /// A new `ArcTester` representing logical OR
1067 ///
1068 /// # Examples
1069 ///
1070 /// ```rust
1071 /// use qubit_function::{ArcTester, Tester};
1072 /// use std::sync::{Arc, atomic::{AtomicUsize, AtomicBool, Ordering}};
1073 /// use std::thread;
1074 ///
1075 /// // Simulate load balancer status
1076 /// let server_load = Arc::new(AtomicUsize::new(0));
1077 /// let is_server_healthy = Arc::new(AtomicBool::new(true));
1078 /// let max_load = 80;
1079 /// let emergency_mode = Arc::new(AtomicBool::new(false));
1080 ///
1081 /// let load_clone = Arc::clone(&server_load);
1082 /// let health_clone = Arc::clone(&is_server_healthy);
1083 /// let emergency_clone = Arc::clone(&emergency_mode);
1084 ///
1085 /// // Server low load
1086 /// let low_load = ArcTester::new(move || {
1087 /// load_clone.load(Ordering::Relaxed) < max_load
1088 /// });
1089 ///
1090 /// // Emergency mode check
1091 /// let emergency_check = ArcTester::new(move || {
1092 /// emergency_clone.load(Ordering::Relaxed)
1093 /// });
1094 ///
1095 /// // Server health check
1096 /// let server_healthy = ArcTester::new(move || {
1097 /// health_clone.load(Ordering::Relaxed)
1098 /// });
1099 ///
1100 /// // Emergency mode or server healthy
1101 /// let can_handle_requests = emergency_check.or(&server_healthy);
1102 ///
1103 /// // Combined condition: low load or can handle requests
1104 /// let should_route_here = low_load.or(&can_handle_requests);
1105 ///
1106 /// // Multi-threaded test
1107 /// let router_clone = should_route_here.clone();
1108 /// let handle = thread::spawn(move || {
1109 /// router_clone.test()
1110 /// });
1111 ///
1112 /// // Initial state: low load and healthy
1113 /// assert!(handle.join().unwrap());
1114 /// assert!(should_route_here.test());
1115 ///
1116 /// // High load but server healthy
1117 /// server_load.store(90, Ordering::Relaxed);
1118 /// assert!(should_route_here.test()); // still healthy
1119 ///
1120 /// // Server unhealthy but emergency mode
1121 /// is_server_healthy.store(false, Ordering::Relaxed);
1122 /// emergency_mode.store(true, Ordering::Relaxed);
1123 /// assert!(should_route_here.test()); // emergency mode
1124 ///
1125 /// // Unhealthy and not emergency mode
1126 /// emergency_mode.store(false, Ordering::Relaxed);
1127 /// assert!(!should_route_here.test());
1128 /// ```
1129 #[inline]
1130 pub fn or(&self, next: &ArcTester) -> ArcTester {
1131 let self_fn = Arc::clone(&self.function);
1132 let next_fn = Arc::clone(&next.function);
1133 ArcTester {
1134 function: Arc::new(move || self_fn() || next_fn()),
1135 }
1136 }
1137
1138 /// Negates the result of this tester
1139 ///
1140 /// Returns a new `ArcTester` that returns the opposite value of the
1141 /// original test result. Borrows `&self`, so the original tester remains
1142 /// available.
1143 ///
1144 /// # Return Value
1145 ///
1146 /// A new `ArcTester` representing logical NOT
1147 ///
1148 /// # Examples
1149 ///
1150 /// ```rust
1151 /// use qubit_function::{ArcTester, Tester};
1152 /// use std::sync::{Arc, atomic::{AtomicUsize, Ordering}};
1153 /// use std::thread;
1154 ///
1155 /// // Simulate task queue status
1156 /// let pending_tasks = Arc::new(AtomicUsize::new(0));
1157 /// let max_queue_size = 100;
1158 ///
1159 /// let tasks_clone = Arc::clone(&pending_tasks);
1160 ///
1161 /// // Queue not full
1162 /// let queue_available = ArcTester::new(move || {
1163 /// tasks_clone.load(Ordering::Relaxed) < max_queue_size
1164 /// });
1165 ///
1166 /// // Queue full (negated)
1167 /// let queue_full = queue_available.not();
1168 ///
1169 /// // Multi-threaded test
1170 /// let queue_full_clone = queue_full.clone();
1171 /// let handle = thread::spawn(move || {
1172 /// queue_full_clone.test()
1173 /// });
1174 ///
1175 /// // Initial state: queue not full
1176 /// pending_tasks.store(50, Ordering::Relaxed);
1177 /// assert!(queue_available.test());
1178 /// assert!(!handle.join().unwrap());
1179 /// assert!(!queue_full.test());
1180 ///
1181 /// // Queue near full
1182 /// pending_tasks.store(95, Ordering::Relaxed);
1183 /// assert!(queue_available.test());
1184 /// assert!(!queue_full.test());
1185 ///
1186 /// // Queue full
1187 /// pending_tasks.store(120, Ordering::Relaxed);
1188 /// assert!(!queue_available.test());
1189 /// assert!(queue_full.test());
1190 /// ```
1191 #[allow(clippy::should_implement_trait)]
1192 #[inline]
1193 pub fn not(&self) -> ArcTester {
1194 let func = Arc::clone(&self.function);
1195 ArcTester {
1196 function: Arc::new(move || !func()),
1197 }
1198 }
1199
1200 /// Combines this tester with another tester using logical NAND
1201 ///
1202 /// Returns a new `ArcTester` that returns `true` unless both tests pass.
1203 /// Borrows `&self`, so the original tester remains available.
1204 ///
1205 /// # Parameters
1206 ///
1207 /// * `next` - The tester to combine with
1208 ///
1209 /// # Return Value
1210 ///
1211 /// A new `ArcTester` representing logical NAND
1212 ///
1213 /// # Examples
1214 ///
1215 /// ```rust
1216 /// use qubit_function::{ArcTester, Tester};
1217 /// use std::sync::{Arc, atomic::{AtomicBool, Ordering}};
1218 /// use std::thread;
1219 ///
1220 /// let flag1 = Arc::new(AtomicBool::new(true));
1221 /// let flag2 = Arc::new(AtomicBool::new(true));
1222 ///
1223 /// let flag1_clone = Arc::clone(&flag1);
1224 /// let flag2_clone = Arc::clone(&flag2);
1225 ///
1226 /// let tester1 = ArcTester::new(move || {
1227 /// flag1_clone.load(Ordering::Relaxed)
1228 /// });
1229 ///
1230 /// let tester2 = ArcTester::new(move || {
1231 /// flag2_clone.load(Ordering::Relaxed)
1232 /// });
1233 ///
1234 /// let nand = tester1.nand(&tester2);
1235 ///
1236 /// // Both true returns false
1237 /// assert!(!nand.test());
1238 ///
1239 /// // At least one false returns true
1240 /// flag1.store(false, Ordering::Relaxed);
1241 /// assert!(nand.test());
1242 ///
1243 /// // Original tester still available
1244 /// assert!(!tester1.test());
1245 /// assert!(tester2.test());
1246 /// ```
1247 #[inline]
1248 pub fn nand(&self, next: &ArcTester) -> ArcTester {
1249 let self_fn = Arc::clone(&self.function);
1250 let next_fn = Arc::clone(&next.function);
1251 ArcTester {
1252 function: Arc::new(move || !(self_fn() && next_fn())),
1253 }
1254 }
1255
1256 /// Combines this tester with another tester using logical XOR
1257 ///
1258 /// Returns a new `ArcTester` that returns `true` if exactly one test
1259 /// passes. Borrows `&self`, so the original tester remains available.
1260 ///
1261 /// # Parameters
1262 ///
1263 /// * `next` - The tester to combine with
1264 ///
1265 /// # Return Value
1266 ///
1267 /// A new `ArcTester` representing logical XOR
1268 ///
1269 /// # Examples
1270 ///
1271 /// ```rust
1272 /// use qubit_function::{ArcTester, Tester};
1273 /// use std::sync::{Arc, atomic::{AtomicBool, Ordering}};
1274 /// use std::thread;
1275 ///
1276 /// let flag1 = Arc::new(AtomicBool::new(true));
1277 /// let flag2 = Arc::new(AtomicBool::new(false));
1278 ///
1279 /// let flag1_clone = Arc::clone(&flag1);
1280 /// let flag2_clone = Arc::clone(&flag2);
1281 ///
1282 /// let tester1 = ArcTester::new(move || {
1283 /// flag1_clone.load(Ordering::Relaxed)
1284 /// });
1285 ///
1286 /// let tester2 = ArcTester::new(move || {
1287 /// flag2_clone.load(Ordering::Relaxed)
1288 /// });
1289 ///
1290 /// let xor = tester1.xor(&tester2);
1291 ///
1292 /// // One true one false returns true
1293 /// assert!(xor.test());
1294 ///
1295 /// // Both true returns false
1296 /// flag2.store(true, Ordering::Relaxed);
1297 /// assert!(!xor.test());
1298 ///
1299 /// // Both false returns false
1300 /// flag1.store(false, Ordering::Relaxed);
1301 /// flag2.store(false, Ordering::Relaxed);
1302 /// assert!(!xor.test());
1303 ///
1304 /// // Original tester still available
1305 /// assert!(!tester1.test());
1306 /// assert!(!tester2.test());
1307 /// ```
1308 #[inline]
1309 pub fn xor(&self, next: &ArcTester) -> ArcTester {
1310 let self_fn = Arc::clone(&self.function);
1311 let next_fn = Arc::clone(&next.function);
1312 ArcTester {
1313 function: Arc::new(move || self_fn() ^ next_fn()),
1314 }
1315 }
1316
1317 /// Combines this tester with another tester using logical NOR
1318 ///
1319 /// Returns a new `ArcTester` that returns `true` only when both tests
1320 /// fail. Borrows `&self`, so the original tester remains available.
1321 ///
1322 /// # Parameters
1323 ///
1324 /// * `next` - The tester to combine with
1325 ///
1326 /// # Return Value
1327 ///
1328 /// A new `ArcTester` representing logical NOR
1329 ///
1330 /// # Examples
1331 ///
1332 /// ```rust
1333 /// use qubit_function::{ArcTester, Tester};
1334 /// use std::sync::{Arc, atomic::{AtomicBool, Ordering}};
1335 /// use std::thread;
1336 ///
1337 /// let flag1 = Arc::new(AtomicBool::new(false));
1338 /// let flag2 = Arc::new(AtomicBool::new(false));
1339 ///
1340 /// let flag1_clone = Arc::clone(&flag1);
1341 /// let flag2_clone = Arc::clone(&flag2);
1342 ///
1343 /// let tester1 = ArcTester::new(move || {
1344 /// flag1_clone.load(Ordering::Relaxed)
1345 /// });
1346 ///
1347 /// let tester2 = ArcTester::new(move || {
1348 /// flag2_clone.load(Ordering::Relaxed)
1349 /// });
1350 ///
1351 /// let nor = tester1.nor(&tester2);
1352 ///
1353 /// // Both false returns true
1354 /// assert!(nor.test());
1355 ///
1356 /// // At least one true returns false
1357 /// flag1.store(true, Ordering::Relaxed);
1358 /// assert!(!nor.test());
1359 ///
1360 /// // Original tester still available
1361 /// assert!(tester1.test());
1362 /// assert!(!tester2.test());
1363 /// ```
1364 #[inline]
1365 pub fn nor(&self, next: &ArcTester) -> ArcTester {
1366 let self_fn = Arc::clone(&self.function);
1367 let next_fn = Arc::clone(&next.function);
1368 ArcTester {
1369 function: Arc::new(move || !(self_fn() || next_fn())),
1370 }
1371 }
1372}
1373
1374impl Tester for ArcTester {
1375 #[inline]
1376 fn test(&self) -> bool {
1377 (self.function)()
1378 }
1379
1380 #[inline]
1381 fn into_box(self) -> BoxTester {
1382 let func = self.function;
1383 BoxTester {
1384 function: Box::new(move || func()),
1385 }
1386 }
1387
1388 #[inline]
1389 fn into_rc(self) -> RcTester {
1390 let func = self.function;
1391 RcTester {
1392 function: Rc::new(move || func()),
1393 }
1394 }
1395
1396 #[inline]
1397 fn into_arc(self) -> ArcTester {
1398 self
1399 }
1400
1401 #[inline]
1402 fn into_fn(self) -> impl Fn() -> bool {
1403 move || (self.function)()
1404 }
1405
1406 #[inline]
1407 fn to_box(&self) -> BoxTester {
1408 let self_fn = self.function.clone();
1409 BoxTester {
1410 function: Box::new(move || self_fn()),
1411 }
1412 }
1413
1414 #[inline]
1415 fn to_rc(&self) -> RcTester {
1416 let self_fn = self.function.clone();
1417 RcTester {
1418 function: Rc::new(move || self_fn()),
1419 }
1420 }
1421
1422 #[inline]
1423 fn to_arc(&self) -> ArcTester {
1424 self.clone()
1425 }
1426
1427 #[inline]
1428 fn to_fn(&self) -> impl Fn() -> bool {
1429 let self_fn = self.function.clone();
1430 move || self_fn()
1431 }
1432}
1433
1434impl Clone for ArcTester {
1435 /// Creates a clone of this `ArcTester`.
1436 ///
1437 /// The cloned instance shares the same underlying function with
1438 /// the original, allowing multiple references to the same test
1439 /// logic.
1440 #[inline]
1441 fn clone(&self) -> Self {
1442 Self {
1443 function: Arc::clone(&self.function),
1444 }
1445 }
1446}
1447
1448// ============================================================================
1449// RcTester: Single-Threaded Shared Ownership Implementation
1450// ============================================================================
1451
1452/// Single-threaded shared ownership Tester implemented using `Rc`
1453///
1454/// `RcTester` wraps a closure in `Rc<dyn Fn() -> bool>`, allowing the tester
1455/// to be cloned and shared within a single thread. Since it doesn't use atomic
1456/// operations, it has lower overhead than `ArcTester`.
1457///
1458/// # Characteristics
1459///
1460/// - **Shared ownership**: Can be cloned
1461/// - **Single-threaded**: Cannot be sent across threads
1462/// - **Low overhead**: Uses `Fn` without needing `RefCell`
1463/// - **Borrowing combination**: `and()`/`or()`/`not()` borrow `&self`
1464///
1465/// # Use Cases
1466///
1467/// - Single-threaded testing scenarios requiring sharing
1468/// - Event-driven systems (single-threaded)
1469/// - Callback-intensive code requiring cloneable tests
1470/// - Performance-sensitive single-threaded code
1471///
1472/// # Examples
1473///
1474/// ```rust
1475/// use qubit_function::{RcTester, Tester};
1476///
1477/// let shared = RcTester::new(|| true);
1478///
1479/// // Clone for multiple uses
1480/// let clone1 = shared.clone();
1481/// let clone2 = shared.clone();
1482///
1483/// // Non-consuming combination
1484/// let combined = shared.and(&clone1);
1485/// ```
1486///
1487/// # Author
1488///
1489/// Haixing Hu
1490pub struct RcTester {
1491 function: Rc<dyn Fn() -> bool>,
1492}
1493
1494impl RcTester {
1495 /// Creates a new `RcTester` from a closure
1496 ///
1497 /// # Type Parameters
1498 ///
1499 /// * `F` - Closure type implementing `Fn() -> bool`
1500 ///
1501 /// # Parameters
1502 ///
1503 /// * `f` - The closure to wrap
1504 ///
1505 /// # Return Value
1506 ///
1507 /// A new `RcTester` instance
1508 ///
1509 /// # Examples
1510 ///
1511 /// ```rust
1512 /// use qubit_function::RcTester;
1513 ///
1514 /// let tester = RcTester::new(|| true);
1515 /// ```
1516 #[inline]
1517 pub fn new<F>(f: F) -> Self
1518 where
1519 F: Fn() -> bool + 'static,
1520 {
1521 RcTester {
1522 function: Rc::new(f),
1523 }
1524 }
1525
1526 /// Combines this tester with another tester using logical AND
1527 ///
1528 /// Returns a new `RcTester` that returns `true` only when both tests
1529 /// pass. Borrows `&self`, so the original tester remains available.
1530 ///
1531 /// # Parameters
1532 ///
1533 /// * `next` - The tester to combine with
1534 ///
1535 /// # Return Value
1536 ///
1537 /// A new `RcTester` representing logical AND
1538 ///
1539 /// # Examples
1540 ///
1541 /// ```rust
1542 /// use qubit_function::{RcTester, Tester};
1543 ///
1544 /// let first = RcTester::new(|| true);
1545 /// let second = RcTester::new(|| true);
1546 /// let combined = first.and(&second);
1547 /// // first and second are still available
1548 /// ```
1549 #[inline]
1550 pub fn and(&self, next: &RcTester) -> RcTester {
1551 let self_fn = Rc::clone(&self.function);
1552 let next_fn = Rc::clone(&next.function);
1553 RcTester {
1554 function: Rc::new(move || self_fn() && next_fn()),
1555 }
1556 }
1557
1558 /// Combines this tester with another tester using logical OR
1559 ///
1560 /// Returns a new `RcTester` that returns `true` if either test passes.
1561 /// Borrows `&self`, so the original tester remains available.
1562 ///
1563 /// # Parameters
1564 ///
1565 /// * `next` - The tester to combine with
1566 ///
1567 /// # Return Value
1568 ///
1569 /// A new `RcTester` representing logical OR
1570 ///
1571 /// # Examples
1572 ///
1573 /// ```rust
1574 /// use qubit_function::{RcTester, Tester};
1575 ///
1576 /// let first = RcTester::new(|| false);
1577 /// let second = RcTester::new(|| true);
1578 /// let combined = first.or(&second);
1579 /// // first and second are still available
1580 /// ```
1581 #[inline]
1582 pub fn or(&self, next: &RcTester) -> RcTester {
1583 let self_fn = Rc::clone(&self.function);
1584 let next_fn = Rc::clone(&next.function);
1585 RcTester {
1586 function: Rc::new(move || self_fn() || next_fn()),
1587 }
1588 }
1589
1590 /// Negates the result of this tester
1591 ///
1592 /// Returns a new `RcTester` that returns the opposite value of the
1593 /// original test result. Borrows `&self`, so the original tester remains
1594 /// available.
1595 ///
1596 /// # Return Value
1597 ///
1598 /// A new `RcTester` representing logical NOT
1599 ///
1600 /// # Examples
1601 ///
1602 /// ```rust
1603 /// use qubit_function::{RcTester, Tester};
1604 ///
1605 /// let original = RcTester::new(|| true);
1606 /// let negated = original.not();
1607 /// // original is still available
1608 /// ```
1609 #[allow(clippy::should_implement_trait)]
1610 #[inline]
1611 pub fn not(&self) -> RcTester {
1612 let self_fn = Rc::clone(&self.function);
1613 RcTester {
1614 function: Rc::new(move || !self_fn()),
1615 }
1616 }
1617
1618 /// Combines this tester with another tester using logical NAND
1619 ///
1620 /// Returns a new `RcTester` that returns `true` unless both tests pass.
1621 /// Borrows `&self`, so the original tester remains available.
1622 ///
1623 /// # Parameters
1624 ///
1625 /// * `next` - The tester to combine with
1626 ///
1627 /// # Return Value
1628 ///
1629 /// A new `RcTester` representing logical NAND
1630 ///
1631 /// # Examples
1632 ///
1633 /// ```rust
1634 /// use qubit_function::{RcTester, Tester};
1635 ///
1636 /// let first = RcTester::new(|| true);
1637 /// let second = RcTester::new(|| true);
1638 /// let nand = first.nand(&second);
1639 ///
1640 /// // Both true returns false
1641 /// assert!(!nand.test());
1642 ///
1643 /// // first and second still available
1644 /// assert!(first.test());
1645 /// assert!(second.test());
1646 /// ```
1647 #[inline]
1648 pub fn nand(&self, next: &RcTester) -> RcTester {
1649 let self_fn = Rc::clone(&self.function);
1650 let next_fn = Rc::clone(&next.function);
1651 RcTester {
1652 function: Rc::new(move || !(self_fn() && next_fn())),
1653 }
1654 }
1655
1656 /// Combines this tester with another tester using logical XOR
1657 ///
1658 /// Returns a new `RcTester` that returns `true` if exactly one test
1659 /// passes. Borrows `&self`, so the original tester remains available.
1660 ///
1661 /// # Parameters
1662 ///
1663 /// * `next` - The tester to combine with
1664 ///
1665 /// # Return Value
1666 ///
1667 /// A new `RcTester` representing logical XOR
1668 ///
1669 /// # Examples
1670 ///
1671 /// ```rust
1672 /// use qubit_function::{RcTester, Tester};
1673 ///
1674 /// let first = RcTester::new(|| true);
1675 /// let second = RcTester::new(|| false);
1676 /// let xor = first.xor(&second);
1677 ///
1678 /// // One true one false returns true
1679 /// assert!(xor.test());
1680 ///
1681 /// // first and second still available
1682 /// assert!(first.test());
1683 /// assert!(!second.test());
1684 /// ```
1685 #[inline]
1686 pub fn xor(&self, next: &RcTester) -> RcTester {
1687 let self_fn = Rc::clone(&self.function);
1688 let next_fn = Rc::clone(&next.function);
1689 RcTester {
1690 function: Rc::new(move || self_fn() ^ next_fn()),
1691 }
1692 }
1693
1694 /// Combines this tester with another tester using logical NOR
1695 ///
1696 /// Returns a new `RcTester` that returns `true` only when both tests
1697 /// fail. Borrows `&self`, so the original tester remains available.
1698 ///
1699 /// # Parameters
1700 ///
1701 /// * `next` - The tester to combine with
1702 ///
1703 /// # Return Value
1704 ///
1705 /// A new `RcTester` representing logical NOR
1706 ///
1707 /// # Examples
1708 ///
1709 /// ```rust
1710 /// use qubit_function::{RcTester, Tester};
1711 ///
1712 /// let first = RcTester::new(|| false);
1713 /// let second = RcTester::new(|| false);
1714 /// let nor = first.nor(&second);
1715 ///
1716 /// // Both false returns true
1717 /// assert!(nor.test());
1718 ///
1719 /// // first and second still available
1720 /// assert!(!first.test());
1721 /// assert!(!second.test());
1722 /// ```
1723 #[inline]
1724 pub fn nor(&self, next: &RcTester) -> RcTester {
1725 let self_fn = Rc::clone(&self.function);
1726 let next_fn = Rc::clone(&next.function);
1727 RcTester {
1728 function: Rc::new(move || !(self_fn() || next_fn())),
1729 }
1730 }
1731}
1732
1733impl Tester for RcTester {
1734 #[inline]
1735 fn test(&self) -> bool {
1736 (self.function)()
1737 }
1738
1739 #[inline]
1740 fn into_box(self) -> BoxTester {
1741 BoxTester {
1742 function: Box::new(move || (self.function)()),
1743 }
1744 }
1745
1746 #[inline]
1747 fn into_rc(self) -> RcTester {
1748 self
1749 }
1750
1751 // Note: RcTester is not Send + Sync, so into_arc() cannot be
1752 // implemented. Calling into_arc() on RcTester will result in a
1753 // compile error due to the Send + Sync trait bounds not being
1754 // satisfied. The default Tester trait implementation will be used.
1755
1756 #[inline]
1757 fn into_fn(self) -> impl Fn() -> bool {
1758 move || (self.function)()
1759 }
1760
1761 #[inline]
1762 fn to_box(&self) -> BoxTester {
1763 let self_fn = self.function.clone();
1764 BoxTester {
1765 function: Box::new(move || self_fn()),
1766 }
1767 }
1768
1769 #[inline]
1770 fn to_rc(&self) -> RcTester {
1771 self.clone()
1772 }
1773
1774 // Note: RcTester is not Send + Sync, so to_arc() cannot be
1775 // implemented. Calling to_arc() on RcTester will result in a compile
1776 // error due to the Send + Sync trait bounds not being satisfied. The
1777 // default Tester trait implementation will be used.
1778
1779 #[inline]
1780 fn to_fn(&self) -> impl Fn() -> bool {
1781 let self_fn = self.function.clone();
1782 move || self_fn()
1783 }
1784}
1785
1786impl Clone for RcTester {
1787 /// Creates a clone of this `RcTester`.
1788 ///
1789 /// The cloned instance shares the same underlying function with
1790 /// the original, allowing multiple references to the same test
1791 /// logic.
1792 #[inline]
1793 fn clone(&self) -> Self {
1794 Self {
1795 function: Rc::clone(&self.function),
1796 }
1797 }
1798}
1799
1800// ============================================================================
1801// Tester Implementation for Closures
1802// ============================================================================
1803
1804impl<F> Tester for F
1805where
1806 F: Fn() -> bool,
1807{
1808 #[inline]
1809 fn test(&self) -> bool {
1810 self()
1811 }
1812
1813 #[inline]
1814 fn into_box(self) -> BoxTester
1815 where
1816 Self: Sized + 'static,
1817 {
1818 BoxTester::new(self)
1819 }
1820
1821 #[inline]
1822 fn into_rc(self) -> RcTester
1823 where
1824 Self: Sized + 'static,
1825 {
1826 RcTester::new(self)
1827 }
1828
1829 #[inline]
1830 fn into_arc(self) -> ArcTester
1831 where
1832 Self: Sized + Send + Sync + 'static,
1833 {
1834 ArcTester::new(self)
1835 }
1836
1837 #[inline]
1838 fn into_fn(self) -> impl Fn() -> bool
1839 where
1840 Self: Sized + 'static,
1841 {
1842 self
1843 }
1844
1845 #[inline]
1846 fn to_box(&self) -> BoxTester
1847 where
1848 Self: Clone + Sized + 'static,
1849 {
1850 self.clone().into_box()
1851 }
1852
1853 #[inline]
1854 fn to_rc(&self) -> RcTester
1855 where
1856 Self: Clone + Sized + 'static,
1857 {
1858 self.clone().into_rc()
1859 }
1860
1861 #[inline]
1862 fn to_arc(&self) -> ArcTester
1863 where
1864 Self: Clone + Sized + Send + Sync + 'static,
1865 {
1866 self.clone().into_arc()
1867 }
1868
1869 #[inline]
1870 fn to_fn(&self) -> impl Fn() -> bool
1871 where
1872 Self: Clone + Sized,
1873 {
1874 self.clone()
1875 }
1876}
1877
1878// ============================================================================
1879// Extension Trait for Convenient Closure Conversion
1880// ============================================================================
1881
1882/// Extension trait providing logical composition methods for closures
1883///
1884/// This trait is automatically implemented for all closures and function
1885/// pointers that match `Fn() -> bool`, enabling method chaining starting
1886/// from a closure.
1887///
1888/// # Examples
1889///
1890/// ```rust
1891/// use qubit_function::{FnTesterOps, Tester};
1892///
1893/// let is_ready = || true;
1894/// let is_available = || true;
1895///
1896/// // Combine testers using extension methods
1897/// let combined = is_ready.and(is_available);
1898/// assert!(combined.test());
1899/// ```
1900///
1901/// # Author
1902///
1903/// Haixing Hu
1904pub trait FnTesterOps: Sized + Fn() -> bool {
1905 /// Returns a tester that represents the logical AND of this tester
1906 /// and another
1907 ///
1908 /// # Parameters
1909 ///
1910 /// * `other` - The other tester to combine with. **Note: This parameter
1911 /// is passed by value and will transfer ownership.** If you need to
1912 /// preserve the original tester, clone it first (if it implements
1913 /// `Clone`). Can be:
1914 /// - Another closure
1915 /// - A function pointer
1916 /// - A `BoxTester`, `RcTester`, or `ArcTester`
1917 ///
1918 /// # Return Value
1919 ///
1920 /// A `BoxTester` representing the logical AND
1921 ///
1922 /// # Examples
1923 ///
1924 /// ```rust
1925 /// use qubit_function::{FnTesterOps, Tester};
1926 ///
1927 /// let is_ready = || true;
1928 /// let is_available = || true;
1929 ///
1930 /// let combined = is_ready.and(is_available);
1931 /// assert!(combined.test());
1932 /// ```
1933 #[inline]
1934 fn and<T>(self, other: T) -> BoxTester
1935 where
1936 Self: 'static,
1937 T: Tester + 'static,
1938 {
1939 BoxTester::new(move || self.test() && other.test())
1940 }
1941
1942 /// Returns a tester that represents the logical OR of this tester
1943 /// and another
1944 ///
1945 /// # Parameters
1946 ///
1947 /// * `other` - The other tester to combine with. **Note: This parameter
1948 /// is passed by value and will transfer ownership.** If you need to
1949 /// preserve the original tester, clone it first (if it implements
1950 /// `Clone`). Can be:
1951 /// - Another closure
1952 /// - A function pointer
1953 /// - A `BoxTester`, `RcTester`, or `ArcTester`
1954 /// - Any type implementing `Tester`
1955 ///
1956 /// # Return Value
1957 ///
1958 /// A `BoxTester` representing the logical OR
1959 ///
1960 /// # Examples
1961 ///
1962 /// ```rust
1963 /// use qubit_function::{FnTesterOps, Tester};
1964 ///
1965 /// let is_ready = || false;
1966 /// let is_fallback = || true;
1967 ///
1968 /// let combined = is_ready.or(is_fallback);
1969 /// assert!(combined.test());
1970 /// ```
1971 #[inline]
1972 fn or<T>(self, other: T) -> BoxTester
1973 where
1974 Self: 'static,
1975 T: Tester + 'static,
1976 {
1977 BoxTester::new(move || self.test() || other.test())
1978 }
1979
1980 /// Returns a tester that represents the logical negation of this tester
1981 ///
1982 /// # Return Value
1983 ///
1984 /// A `BoxTester` representing the logical negation
1985 ///
1986 /// # Examples
1987 ///
1988 /// ```rust
1989 /// use qubit_function::{FnTesterOps, Tester};
1990 ///
1991 /// let is_ready = || false;
1992 /// let not_ready = is_ready.not();
1993 /// assert!(not_ready.test());
1994 /// ```
1995 #[inline]
1996 fn not(self) -> BoxTester
1997 where
1998 Self: 'static,
1999 {
2000 BoxTester::new(move || !self.test())
2001 }
2002
2003 /// Returns a tester that represents the logical NAND (NOT AND) of this
2004 /// tester and another
2005 ///
2006 /// NAND returns `true` unless both testers are `true`.
2007 /// Equivalent to `!(self AND other)`.
2008 ///
2009 /// # Parameters
2010 ///
2011 /// * `other` - The other tester to combine with. **Note: This parameter
2012 /// is passed by value and will transfer ownership.** If you need to
2013 /// preserve the original tester, clone it first (if it implements
2014 /// `Clone`). Accepts closures, function pointers, or any
2015 /// `Tester` implementation.
2016 ///
2017 /// # Return Value
2018 ///
2019 /// A `BoxTester` representing the logical NAND
2020 ///
2021 /// # Examples
2022 ///
2023 /// ```rust
2024 /// use qubit_function::{FnTesterOps, Tester};
2025 ///
2026 /// let is_ready = || true;
2027 /// let is_available = || true;
2028 ///
2029 /// let nand = is_ready.nand(is_available);
2030 /// assert!(!nand.test()); // !(true && true) = false
2031 /// ```
2032 #[inline]
2033 fn nand<T>(self, other: T) -> BoxTester
2034 where
2035 Self: 'static,
2036 T: Tester + 'static,
2037 {
2038 BoxTester::new(move || !(self.test() && other.test()))
2039 }
2040
2041 /// Returns a tester that represents the logical XOR (exclusive OR) of
2042 /// this tester and another
2043 ///
2044 /// XOR returns `true` if exactly one of the testers is `true`.
2045 ///
2046 /// # Parameters
2047 ///
2048 /// * `other` - The other tester to combine with. **Note: This parameter
2049 /// is passed by value and will transfer ownership.** If you need to
2050 /// preserve the original tester, clone it first (if it implements
2051 /// `Clone`). Accepts closures, function pointers, or any
2052 /// `Tester` implementation.
2053 ///
2054 /// # Return Value
2055 ///
2056 /// A `BoxTester` representing the logical XOR
2057 ///
2058 /// # Examples
2059 ///
2060 /// ```rust
2061 /// use qubit_function::{FnTesterOps, Tester};
2062 ///
2063 /// let is_ready = || true;
2064 /// let is_available = || false;
2065 ///
2066 /// let xor = is_ready.xor(is_available);
2067 /// assert!(xor.test()); // true ^ false = true
2068 /// ```
2069 #[inline]
2070 fn xor<T>(self, other: T) -> BoxTester
2071 where
2072 Self: 'static,
2073 T: Tester + 'static,
2074 {
2075 BoxTester::new(move || self.test() ^ other.test())
2076 }
2077
2078 /// Returns a tester that represents the logical NOR (NOT OR) of this
2079 /// tester and another
2080 ///
2081 /// NOR returns `true` only when both testers are `false`. Equivalent
2082 /// to `!(self OR other)`.
2083 ///
2084 /// # Parameters
2085 ///
2086 /// * `other` - The other tester to combine with. **Note: This parameter
2087 /// is passed by value and will transfer ownership.** If you need to
2088 /// preserve the original tester, clone it first (if it implements
2089 /// `Clone`). Accepts closures, function pointers, or any
2090 /// `Tester` implementation.
2091 ///
2092 /// # Return Value
2093 ///
2094 /// A `BoxTester` representing the logical NOR
2095 ///
2096 /// # Examples
2097 ///
2098 /// ```rust
2099 /// use qubit_function::{FnTesterOps, Tester};
2100 ///
2101 /// let is_ready = || false;
2102 /// let is_available = || false;
2103 ///
2104 /// let nor = is_ready.nor(is_available);
2105 /// assert!(nor.test()); // !(false || false) = true
2106 /// ```
2107 #[inline]
2108 fn nor<T>(self, other: T) -> BoxTester
2109 where
2110 Self: 'static,
2111 T: Tester + 'static,
2112 {
2113 BoxTester::new(move || !(self.test() || other.test()))
2114 }
2115}
2116
2117// Blanket implementation for all closures
2118impl<F> FnTesterOps for F where F: Fn() -> bool {}