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
163mod box_tester;
164pub use box_tester::BoxTester;
165mod arc_tester;
166pub use arc_tester::ArcTester;
167mod rc_tester;
168pub use rc_tester::RcTester;
169mod fn_tester_ops;
170pub use fn_tester_ops::FnTesterOps;
171
172// ============================================================================
173// Core Tester Trait
174// ============================================================================
175
176/// Tests whether a state or condition holds
177///
178/// Tester is a functional abstraction for testing states or conditions. It
179/// accepts no parameters and returns a boolean value indicating the test
180/// result of some state or condition.
181///
182/// # Core Characteristics
183///
184/// - **No input parameters**: Captures context through closures
185/// - **Returns boolean**: Indicates test results
186/// - **Uses `&self`**: Does not modify its own state, only reads external
187/// state
188/// - **Repeatable calls**: The same Tester can call `test()` multiple times
189///
190/// # Use Cases
191///
192/// - **State checking**: Check system or service status
193/// - **Condition waiting**: Repeatedly check until conditions are met
194/// - **Health monitoring**: Check system health status
195/// - **Precondition validation**: Verify conditions before operations
196///
197/// # Design Philosophy
198///
199/// Tester's responsibility is "test judgment", not "state management".
200/// State management is the caller's responsibility. Tester only reads state
201/// and returns judgment results.
202///
203/// # Examples
204///
205/// ```rust
206/// use qubit_function::{BoxTester, Tester};
207/// use std::sync::{Arc, atomic::{AtomicBool, Ordering}};
208///
209/// // State managed externally
210/// let ready = Arc::new(AtomicBool::new(false));
211/// let ready_clone = Arc::clone(&ready);
212///
213/// // Tester only responsible for reading state
214/// let tester = BoxTester::new(move || {
215/// ready_clone.load(Ordering::Acquire)
216/// });
217///
218/// // Can be called multiple times
219/// assert!(!tester.test());
220/// ready.store(true, Ordering::Release);
221/// assert!(tester.test());
222/// ```
223///
224/// # Author
225///
226/// Haixing Hu
227pub trait Tester {
228 /// Executes the test and returns the test result
229 ///
230 /// This method can be called multiple times without modifying the Tester's
231 /// own state.
232 ///
233 /// # Return Value
234 ///
235 /// Returns `true` if the condition holds, otherwise returns `false`
236 ///
237 /// # Examples
238 ///
239 /// ```rust
240 /// use qubit_function::{BoxTester, Tester};
241 ///
242 /// let tester = BoxTester::new(|| true);
243 /// assert!(tester.test());
244 /// ```
245 fn test(&self) -> bool;
246
247 /// Converts this tester to `BoxTester`
248 ///
249 /// # Return Value
250 ///
251 /// A `BoxTester` that wraps this tester
252 ///
253 /// # Examples
254 ///
255 /// ```rust
256 /// use qubit_function::{Tester, BoxTester};
257 ///
258 /// let closure = || true;
259 /// let boxed: BoxTester = closure.into_box();
260 /// ```
261 #[inline]
262 fn into_box(self) -> BoxTester
263 where
264 Self: Sized + 'static,
265 {
266 BoxTester {
267 function: Box::new(move || self.test()),
268 }
269 }
270
271 /// Converts this tester to `RcTester`
272 ///
273 /// # Return Value
274 ///
275 /// A `RcTester` that wraps this tester
276 ///
277 /// # Examples
278 ///
279 /// ```rust
280 /// use qubit_function::{Tester, RcTester};
281 ///
282 /// let closure = || true;
283 /// let rc: RcTester = closure.into_rc();
284 /// ```
285 #[inline]
286 fn into_rc(self) -> RcTester
287 where
288 Self: Sized + 'static,
289 {
290 RcTester {
291 function: Rc::new(move || self.test()),
292 }
293 }
294
295 /// Converts this tester to `ArcTester`
296 ///
297 /// # Return Value
298 ///
299 /// An `ArcTester` that wraps this tester
300 ///
301 /// # Examples
302 ///
303 /// ```rust
304 /// use qubit_function::{Tester, ArcTester};
305 ///
306 /// let closure = || true;
307 /// let arc: ArcTester = closure.into_arc();
308 /// ```
309 #[inline]
310 fn into_arc(self) -> ArcTester
311 where
312 Self: Sized + Send + Sync + 'static,
313 {
314 ArcTester {
315 function: Arc::new(move || self.test()),
316 }
317 }
318
319 /// Converts this tester to a boxed function pointer
320 ///
321 /// # Return Value
322 ///
323 /// A `Box<dyn Fn() -> bool>` that wraps this tester
324 ///
325 /// # Examples
326 ///
327 /// ```rust
328 /// use qubit_function::Tester;
329 ///
330 /// let closure = || true;
331 /// let func = closure.into_fn();
332 /// assert!(func());
333 /// ```
334 #[inline]
335 fn into_fn(self) -> impl Fn() -> bool
336 where
337 Self: Sized + 'static,
338 {
339 Box::new(move || self.test())
340 }
341
342 /// Clones and converts this tester to `BoxTester`
343 ///
344 /// # Return Value
345 ///
346 /// A `BoxTester` that wraps a clone of this tester
347 ///
348 /// # Examples
349 ///
350 /// ```rust
351 /// use qubit_function::{Tester, BoxTester, ArcTester};
352 ///
353 /// let arc = ArcTester::new(|| true);
354 /// let boxed: BoxTester = arc.to_box();
355 /// // arc is still available
356 /// ```
357 #[inline]
358 fn to_box(&self) -> BoxTester
359 where
360 Self: Clone + 'static,
361 {
362 self.clone().into_box()
363 }
364
365 /// Clones and converts this tester to `RcTester`
366 ///
367 /// # Return Value
368 ///
369 /// A `RcTester` that wraps a clone of this tester
370 ///
371 /// # Examples
372 ///
373 /// ```rust
374 /// use qubit_function::{Tester, RcTester, ArcTester};
375 ///
376 /// let arc = ArcTester::new(|| true);
377 /// let rc: RcTester = arc.to_rc();
378 /// // arc is still available
379 /// ```
380 #[inline]
381 fn to_rc(&self) -> RcTester
382 where
383 Self: Clone + 'static,
384 {
385 self.clone().into_rc()
386 }
387
388 /// Clones and converts this tester to `ArcTester`
389 ///
390 /// # Return Value
391 ///
392 /// An `ArcTester` that wraps a clone of this tester
393 ///
394 /// # Examples
395 ///
396 /// ```rust
397 /// use qubit_function::{Tester, ArcTester, RcTester};
398 ///
399 /// let rc = RcTester::new(|| true);
400 /// // Note: This will panic for RcTester as it's not Send + Sync
401 /// // let arc: ArcTester = rc.to_arc();
402 /// ```
403 #[inline]
404 fn to_arc(&self) -> ArcTester
405 where
406 Self: Clone + Send + Sync + 'static,
407 {
408 self.clone().into_arc()
409 }
410
411 /// Clones and converts this tester to a boxed function pointer
412 ///
413 /// # Return Value
414 ///
415 /// A `Box<dyn Fn() -> bool>` that wraps a clone of this tester
416 ///
417 /// # Examples
418 ///
419 /// ```rust
420 /// use qubit_function::{Tester, ArcTester};
421 ///
422 /// let arc = ArcTester::new(|| true);
423 /// let func = arc.to_fn();
424 /// // arc is still available
425 /// assert!(func());
426 /// ```
427 #[inline]
428 fn to_fn(&self) -> impl Fn() -> bool
429 where
430 Self: Clone + 'static,
431 {
432 self.clone().into_fn()
433 }
434}