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}