Skip to main content

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}