Skip to main content

qubit_function/
testers.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//! # Tester Types
11//!
12//! Provides zero-argument tester abstractions that return boolean values.
13//!
14//! # Overview
15//!
16//! **Tester** is the stateless zero-argument tester abstraction. It corresponds
17//! to Rust's `Fn() -> bool`: callers may execute it repeatedly through `&self`,
18//! and the implementation should not require mutable access to its own state.
19//!
20//! **StatefulTester** is the stateful zero-argument tester abstraction. It
21//! corresponds to Rust's `FnMut() -> bool`: callers execute it through
22//! `&mut self`, allowing the implementation to update captured or internal
23//! state between test calls.
24//!
25//! # Core Design Principles
26//!
27//! 1. **Returns boolean**: `Tester` returns `bool` to indicate test results
28//! 2. **Uses `&self`**: `Tester` represents `Fn() -> bool` and does not need
29//!    mutable access to run
30//! 3. **No TesterOnce**: Very limited use cases, lacks practical examples
31//! 4. **Separate stateful API**: Use `StatefulTester` for `FnMut() -> bool`
32//!    closures or custom testers that mutate internal state
33//!
34//! # Three Implementations
35//!
36//! - **`BoxTester`** / **`BoxStatefulTester`**: Single ownership using
37//!   `Box<dyn Fn() -> bool>` or `Box<dyn FnMut() -> bool>`. Best for one-time
38//!   use and builder patterns.
39//!
40//! - **`ArcTester`** / **`ArcStatefulTester`**: Thread-safe shared ownership.
41//!   Stateless testers use `Arc<dyn Fn() -> bool + Send + Sync>`, while
42//!   stateful testers use `Arc<Mutex<dyn FnMut() -> bool + Send>>`.
43//!
44//! - **`RcTester`** / **`RcStatefulTester`**: Single-threaded shared ownership.
45//!   Stateless testers use `Rc<dyn Fn() -> bool>`, while stateful testers use
46//!   `Rc<RefCell<dyn FnMut() -> bool>>`.
47//!
48//! # Comparison with Other Functional Abstractions
49//!
50//! | Type            | Input | Output | self        | Closure Shape     | Use Cases   |
51//! |-----------------|-------|--------|-------------|-------------------|-------------|
52//! | Tester          | None  | `bool` | `&self`     | `Fn() -> bool`    | State Check |
53//! | StatefulTester  | None  | `bool` | `&mut self` | `FnMut() -> bool` | Stateful Check |
54//! | Predicate       | `&T`  | `bool` | `&self`     | `Fn(&T) -> bool`  | Filter      |
55//! | Supplier        | None  | `T`    | `&mut self` | `FnMut() -> T`    | Factory     |
56//!
57//! # Examples
58//!
59//! ## Basic State Checking
60//!
61//! ```rust
62//! use qubit_function::{BoxTester, Tester};
63//! use std::sync::{Arc, atomic::{AtomicUsize, Ordering}};
64//!
65//! // State managed externally
66//! let count = Arc::new(AtomicUsize::new(0));
67//! let count_clone = Arc::clone(&count);
68//!
69//! let tester = BoxTester::new(move || {
70//!     count_clone.load(Ordering::Relaxed) <= 3
71//! });
72//!
73//! assert!(tester.test());  // true (0)
74//! count.fetch_add(1, Ordering::Relaxed);
75//! assert!(tester.test());  // true (1)
76//! count.fetch_add(1, Ordering::Relaxed);
77//! assert!(tester.test());  // true (2)
78//! count.fetch_add(1, Ordering::Relaxed);
79//! assert!(tester.test());  // true (3)
80//! count.fetch_add(1, Ordering::Relaxed);
81//! assert!(!tester.test()); // false (4)
82//! ```
83//!
84//! ## Logical Combination
85//!
86//! ```rust
87//! use qubit_function::{BoxTester, Tester};
88//! use std::sync::{Arc, atomic::{AtomicUsize, AtomicBool, Ordering}};
89//!
90//! // Simulate microservice health check scenario
91//! let cpu_usage = Arc::new(AtomicUsize::new(0));
92//! let memory_usage = Arc::new(AtomicUsize::new(0));
93//! let is_healthy = Arc::new(AtomicBool::new(true));
94//! let is_ready = Arc::new(AtomicBool::new(false));
95//! let max_cpu = 80;
96//! let max_memory = 90;
97//!
98//! let cpu_clone = Arc::clone(&cpu_usage);
99//! let memory_clone = Arc::clone(&memory_usage);
100//! let health_clone = Arc::clone(&is_healthy);
101//! let ready_clone = Arc::clone(&is_ready);
102//!
103//! // System resource check: CPU and memory within safe limits
104//! let resources_ok = BoxTester::new(move || {
105//!     cpu_clone.load(Ordering::Relaxed) < max_cpu
106//! })
107//! .and(move || {
108//!     memory_clone.load(Ordering::Relaxed) < max_memory
109//! });
110//!
111//! // Service status check: healthy or ready
112//! let service_ok = BoxTester::new(move || {
113//!     health_clone.load(Ordering::Relaxed)
114//! })
115//! .or(move || {
116//!     ready_clone.load(Ordering::Relaxed)
117//! });
118//!
119//! // Combined condition: resources normal and service available
120//! let can_accept_traffic = resources_ok.and(service_ok);
121//!
122//! // Test different state combinations
123//! // Initial state: resources normal and service healthy
124//! cpu_usage.store(50, Ordering::Relaxed);
125//! memory_usage.store(60, Ordering::Relaxed);
126//! assert!(can_accept_traffic.test()); // resources normal and service healthy
127//!
128//! // Service unhealthy but ready
129//! is_healthy.store(false, Ordering::Relaxed);
130//! is_ready.store(true, Ordering::Relaxed);
131//! assert!(can_accept_traffic.test()); // resources normal and service ready
132//!
133//! // CPU usage too high
134//! cpu_usage.store(95, Ordering::Relaxed);
135//! assert!(!can_accept_traffic.test()); // resources exceeded
136//!
137//! // Service unhealthy but ready
138//! is_healthy.store(false, Ordering::Relaxed);
139//! cpu_usage.store(50, Ordering::Relaxed);
140//! assert!(can_accept_traffic.test()); // still ready
141//! ```
142//!
143//! ## Thread-Safe Sharing
144//!
145//! ```rust
146//! use qubit_function::{ArcTester, Tester};
147//! use std::thread;
148//!
149//! let shared = ArcTester::new(|| true);
150//! let clone = shared.clone();
151//!
152//! let handle = thread::spawn(move || {
153//!     clone.test()
154//! });
155//!
156//! assert!(handle.join().expect("thread should not panic"));
157//! ```
158//!
159use std::rc::Rc;
160use std::sync::Arc;
161
162pub mod stateful_tester;
163pub mod tester;