Skip to main content

qubit_function/
tester.rs

1/*******************************************************************************
2 *
3 *    Copyright (c) 2025 - 2026 Haixing Hu.
4 *
5 *    SPDX-License-Identifier: Apache-2.0
6 *
7 *    Licensed under the Apache License, Version 2.0.
8 *
9 ******************************************************************************/
10//! # Tester Type
11//!
12//! Provides tester implementations that test conditions or states and return
13//! boolean values, without accepting input parameters.
14//!
15//! # Overview
16//!
17//! **Tester** is a functional abstraction for testing conditions or states
18//! without accepting input. It can check system status, wait for conditions,
19//! or perform health checks.
20//!
21//! This module implements **Option 3** from the design document: a unified
22//! `Tester` trait with multiple concrete implementations optimized for
23//! different ownership and concurrency scenarios.
24//!
25//! # Core Design Principles
26//!
27//! 1. **Returns boolean**: `Tester` returns `bool` to indicate test results
28//! 2. **Uses `&self`**: Tester is only responsible for "judgment", not
29//!    "state management"
30//! 3. **No TesterOnce**: Very limited use cases, lacks practical examples
31//! 4. **State management is caller's responsibility**: Tester only reads
32//!    state, does not modify state
33//!
34//! # Three Implementations
35//!
36//! - **`BoxTester`**: Single ownership using `Box<dyn Fn() -> bool>`.
37//!   Zero overhead, cannot be cloned. Best for one-time use and builder
38//!   patterns.
39//!
40//! - **`ArcTester`**: Thread-safe shared ownership using
41//!   `Arc<dyn Fn() -> bool + Send + Sync>`. Can be cloned and sent across
42//!   threads. Lock-free overhead.
43//!
44//! - **`RcTester`**: Single-threaded shared ownership using
45//!   `Rc<dyn Fn() -> bool>`. Can be cloned but cannot be sent across
46//!   threads. Lower overhead than `ArcTester`.
47//!
48//! # Comparison with Other Functional Abstractions
49//!
50//! | Type      | Input | Output | self      | Modify? | Use Cases   |
51//! |-----------|-------|--------|-----------|---------|-------------|
52//! | Tester    | None  | `bool` | `&self`   | No      | State Check |
53//! | Predicate | `&T`  | `bool` | `&self`   | No      | Filter      |
54//! | Supplier  | None  | `T`    | `&mut`    | Yes     | Factory     |
55//!
56//! # Examples
57//!
58//! ## Basic State Checking
59//!
60//! ```rust
61//! use qubit_function::{BoxTester, Tester};
62//! use std::sync::{Arc, atomic::{AtomicUsize, Ordering}};
63//!
64//! // State managed externally
65//! let count = Arc::new(AtomicUsize::new(0));
66//! let count_clone = Arc::clone(&count);
67//!
68//! let tester = BoxTester::new(move || {
69//!     count_clone.load(Ordering::Relaxed) <= 3
70//! });
71//!
72//! assert!(tester.test());  // true (0)
73//! count.fetch_add(1, Ordering::Relaxed);
74//! assert!(tester.test());  // true (1)
75//! count.fetch_add(1, Ordering::Relaxed);
76//! assert!(tester.test());  // true (2)
77//! count.fetch_add(1, Ordering::Relaxed);
78//! assert!(tester.test());  // true (3)
79//! count.fetch_add(1, Ordering::Relaxed);
80//! assert!(!tester.test()); // false (4)
81//! ```
82//!
83//! ## Logical Combination
84//!
85//! ```rust
86//! use qubit_function::{BoxTester, Tester};
87//! use std::sync::{Arc, atomic::{AtomicUsize, AtomicBool, Ordering}};
88//!
89//! // Simulate microservice health check scenario
90//! let cpu_usage = Arc::new(AtomicUsize::new(0));
91//! let memory_usage = Arc::new(AtomicUsize::new(0));
92//! let is_healthy = Arc::new(AtomicBool::new(true));
93//! let is_ready = Arc::new(AtomicBool::new(false));
94//! let max_cpu = 80;
95//! let max_memory = 90;
96//!
97//! let cpu_clone = Arc::clone(&cpu_usage);
98//! let memory_clone = Arc::clone(&memory_usage);
99//! let health_clone = Arc::clone(&is_healthy);
100//! let ready_clone = Arc::clone(&is_ready);
101//!
102//! // System resource check: CPU and memory within safe limits
103//! let resources_ok = BoxTester::new(move || {
104//!     cpu_clone.load(Ordering::Relaxed) < max_cpu
105//! })
106//! .and(move || {
107//!     memory_clone.load(Ordering::Relaxed) < max_memory
108//! });
109//!
110//! // Service status check: healthy or ready
111//! let service_ok = BoxTester::new(move || {
112//!     health_clone.load(Ordering::Relaxed)
113//! })
114//! .or(move || {
115//!     ready_clone.load(Ordering::Relaxed)
116//! });
117//!
118//! // Combined condition: resources normal and service available
119//! let can_accept_traffic = resources_ok.and(service_ok);
120//!
121//! // Test different state combinations
122//! // Initial state: resources normal and service healthy
123//! cpu_usage.store(50, Ordering::Relaxed);
124//! memory_usage.store(60, Ordering::Relaxed);
125//! assert!(can_accept_traffic.test()); // resources normal and service healthy
126//!
127//! // Service unhealthy but ready
128//! is_healthy.store(false, Ordering::Relaxed);
129//! is_ready.store(true, Ordering::Relaxed);
130//! assert!(can_accept_traffic.test()); // resources normal and service ready
131//!
132//! // CPU usage too high
133//! cpu_usage.store(95, Ordering::Relaxed);
134//! assert!(!can_accept_traffic.test()); // resources exceeded
135//!
136//! // Service unhealthy but ready
137//! is_healthy.store(false, Ordering::Relaxed);
138//! cpu_usage.store(50, Ordering::Relaxed);
139//! assert!(can_accept_traffic.test()); // still ready
140//! ```
141//!
142//! ## Thread-Safe Sharing
143//!
144//! ```rust
145//! use qubit_function::{ArcTester, Tester};
146//! use std::thread;
147//!
148//! let shared = ArcTester::new(|| true);
149//! let clone = shared.clone();
150//!
151//! let handle = thread::spawn(move || {
152//!     clone.test()
153//! });
154//!
155//! assert!(handle.join().unwrap());
156//! ```
157//!
158use std::rc::Rc;
159use std::sync::Arc;
160
161mod box_tester;
162pub use box_tester::BoxTester;
163mod arc_tester;
164pub use arc_tester::ArcTester;
165mod rc_tester;
166pub use rc_tester::RcTester;
167mod fn_tester_ops;
168pub use fn_tester_ops::FnTesterOps;
169
170// ============================================================================
171// Core Tester Trait
172// ============================================================================
173
174/// Tests whether a state or condition holds
175///
176/// Tester is a functional abstraction for testing states or conditions. It
177/// accepts no parameters and returns a boolean value indicating the test
178/// result of some state or condition.
179///
180/// # Core Characteristics
181///
182/// - **No input parameters**: Captures context through closures
183/// - **Returns boolean**: Indicates test results
184/// - **Uses `&self`**: Does not modify its own state, only reads external
185///   state
186/// - **Repeatable calls**: The same Tester can call `test()` multiple times
187///
188/// # Use Cases
189///
190/// - **State checking**: Check system or service status
191/// - **Condition waiting**: Repeatedly check until conditions are met
192/// - **Health monitoring**: Check system health status
193/// - **Precondition validation**: Verify conditions before operations
194///
195/// # Design Philosophy
196///
197/// Tester's responsibility is "test judgment", not "state management".
198/// State management is the caller's responsibility. Tester only reads state
199/// and returns judgment results.
200///
201/// # Examples
202///
203/// ```rust
204/// use qubit_function::{BoxTester, Tester};
205/// use std::sync::{Arc, atomic::{AtomicBool, Ordering}};
206///
207/// // State managed externally
208/// let ready = Arc::new(AtomicBool::new(false));
209/// let ready_clone = Arc::clone(&ready);
210///
211/// // Tester only responsible for reading state
212/// let tester = BoxTester::new(move || {
213///     ready_clone.load(Ordering::Acquire)
214/// });
215///
216/// // Can be called multiple times
217/// assert!(!tester.test());
218/// ready.store(true, Ordering::Release);
219/// assert!(tester.test());
220/// ```
221///
222pub trait Tester {
223    /// Executes the test and returns the test result
224    ///
225    /// This method can be called multiple times without modifying the Tester's
226    /// own state.
227    ///
228    /// # Return Value
229    ///
230    /// Returns `true` if the condition holds, otherwise returns `false`
231    ///
232    /// # Examples
233    ///
234    /// ```rust
235    /// use qubit_function::{BoxTester, Tester};
236    ///
237    /// let tester = BoxTester::new(|| true);
238    /// assert!(tester.test());
239    /// ```
240    fn test(&self) -> bool;
241
242    /// Converts this tester to `BoxTester`
243    ///
244    /// # Return Value
245    ///
246    /// A `BoxTester` that wraps this tester
247    ///
248    /// # Examples
249    ///
250    /// ```rust
251    /// use qubit_function::{Tester, BoxTester};
252    ///
253    /// let closure = || true;
254    /// let boxed: BoxTester = closure.into_box();
255    /// ```
256    #[inline]
257    fn into_box(self) -> BoxTester
258    where
259        Self: Sized + 'static,
260    {
261        BoxTester {
262            function: Box::new(move || self.test()),
263        }
264    }
265
266    /// Converts this tester to `RcTester`
267    ///
268    /// # Return Value
269    ///
270    /// A `RcTester` that wraps this tester
271    ///
272    /// # Examples
273    ///
274    /// ```rust
275    /// use qubit_function::{Tester, RcTester};
276    ///
277    /// let closure = || true;
278    /// let rc: RcTester = closure.into_rc();
279    /// ```
280    #[inline]
281    fn into_rc(self) -> RcTester
282    where
283        Self: Sized + 'static,
284    {
285        RcTester {
286            function: Rc::new(move || self.test()),
287        }
288    }
289
290    /// Converts this tester to `ArcTester`
291    ///
292    /// # Return Value
293    ///
294    /// An `ArcTester` that wraps this tester
295    ///
296    /// # Examples
297    ///
298    /// ```rust
299    /// use qubit_function::{Tester, ArcTester};
300    ///
301    /// let closure = || true;
302    /// let arc: ArcTester = closure.into_arc();
303    /// ```
304    #[inline]
305    fn into_arc(self) -> ArcTester
306    where
307        Self: Sized + Send + Sync + 'static,
308    {
309        ArcTester {
310            function: Arc::new(move || self.test()),
311        }
312    }
313
314    /// Converts this tester to a boxed function pointer
315    ///
316    /// # Return Value
317    ///
318    /// A `Box<dyn Fn() -> bool>` that wraps this tester
319    ///
320    /// # Examples
321    ///
322    /// ```rust
323    /// use qubit_function::Tester;
324    ///
325    /// let closure = || true;
326    /// let func = closure.into_fn();
327    /// assert!(func());
328    /// ```
329    #[inline]
330    fn into_fn(self) -> impl Fn() -> bool
331    where
332        Self: Sized + 'static,
333    {
334        Box::new(move || self.test())
335    }
336
337    /// Clones and converts this tester to `BoxTester`
338    ///
339    /// # Return Value
340    ///
341    /// A `BoxTester` that wraps a clone of this tester
342    ///
343    /// # Examples
344    ///
345    /// ```rust
346    /// use qubit_function::{Tester, BoxTester, ArcTester};
347    ///
348    /// let arc = ArcTester::new(|| true);
349    /// let boxed: BoxTester = arc.to_box();
350    /// // arc is still available
351    /// ```
352    #[inline]
353    fn to_box(&self) -> BoxTester
354    where
355        Self: Clone + 'static,
356    {
357        self.clone().into_box()
358    }
359
360    /// Clones and converts this tester to `RcTester`
361    ///
362    /// # Return Value
363    ///
364    /// A `RcTester` that wraps a clone of this tester
365    ///
366    /// # Examples
367    ///
368    /// ```rust
369    /// use qubit_function::{Tester, RcTester, ArcTester};
370    ///
371    /// let arc = ArcTester::new(|| true);
372    /// let rc: RcTester = arc.to_rc();
373    /// // arc is still available
374    /// ```
375    #[inline]
376    fn to_rc(&self) -> RcTester
377    where
378        Self: Clone + 'static,
379    {
380        self.clone().into_rc()
381    }
382
383    /// Clones and converts this tester to `ArcTester`
384    ///
385    /// # Return Value
386    ///
387    /// An `ArcTester` that wraps a clone of this tester
388    ///
389    /// # Examples
390    ///
391    /// ```rust
392    /// use qubit_function::{Tester, ArcTester, RcTester};
393    ///
394    /// let rc = RcTester::new(|| true);
395    /// // Note: This will panic for RcTester as it's not Send + Sync
396    /// // let arc: ArcTester = rc.to_arc();
397    /// ```
398    #[inline]
399    fn to_arc(&self) -> ArcTester
400    where
401        Self: Clone + Send + Sync + 'static,
402    {
403        self.clone().into_arc()
404    }
405
406    /// Clones and converts this tester to a boxed function pointer
407    ///
408    /// # Return Value
409    ///
410    /// A `Box<dyn Fn() -> bool>` that wraps a clone of this tester
411    ///
412    /// # Examples
413    ///
414    /// ```rust
415    /// use qubit_function::{Tester, ArcTester};
416    ///
417    /// let arc = ArcTester::new(|| true);
418    /// let func = arc.to_fn();
419    /// // arc is still available
420    /// assert!(func());
421    /// ```
422    #[inline]
423    fn to_fn(&self) -> impl Fn() -> bool
424    where
425        Self: Clone + 'static,
426    {
427        self.clone().into_fn()
428    }
429}