prism3_function/tester.rs
1/*******************************************************************************
2 *
3 * Copyright (c) 2025.
4 * 3-Prism 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 prism3_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 prism3_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 prism3_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//! Hu Haixing
160
161use std::rc::Rc;
162use std::sync::Arc;
163
164// ============================================================================
165// Core Tester Trait
166// ============================================================================
167
168/// Tests whether a state or condition holds
169///
170/// Tester is a functional abstraction for testing states or conditions. It
171/// accepts no parameters and returns a boolean value indicating the test
172/// result of some state or condition.
173///
174/// # Core Characteristics
175///
176/// - **No input parameters**: Captures context through closures
177/// - **Returns boolean**: Indicates test results
178/// - **Uses `&self`**: Does not modify its own state, only reads external
179/// state
180/// - **Repeatable calls**: The same Tester can call `test()` multiple times
181///
182/// # Use Cases
183///
184/// - **State checking**: Check system or service status
185/// - **Condition waiting**: Repeatedly check until conditions are met
186/// - **Health monitoring**: Check system health status
187/// - **Precondition validation**: Verify conditions before operations
188///
189/// # Design Philosophy
190///
191/// Tester's responsibility is "test judgment", not "state management".
192/// State management is the caller's responsibility. Tester only reads state
193/// and returns judgment results.
194///
195/// # Examples
196///
197/// ```rust
198/// use prism3_function::{BoxTester, Tester};
199/// use std::sync::{Arc, atomic::{AtomicBool, Ordering}};
200///
201/// // State managed externally
202/// let ready = Arc::new(AtomicBool::new(false));
203/// let ready_clone = Arc::clone(&ready);
204///
205/// // Tester only responsible for reading state
206/// let tester = BoxTester::new(move || {
207/// ready_clone.load(Ordering::Acquire)
208/// });
209///
210/// // Can be called multiple times
211/// assert!(!tester.test());
212/// ready.store(true, Ordering::Release);
213/// assert!(tester.test());
214/// ```
215///
216/// # Author
217///
218/// Hu Haixing
219pub trait Tester {
220 /// Executes the test and returns the test result
221 ///
222 /// This method can be called multiple times without modifying the Tester's
223 /// own state.
224 ///
225 /// # Return Value
226 ///
227 /// Returns `true` if the condition holds, otherwise returns `false`
228 ///
229 /// # Examples
230 ///
231 /// ```rust
232 /// use prism3_function::{BoxTester, Tester};
233 ///
234 /// let tester = BoxTester::new(|| true);
235 /// assert!(tester.test());
236 /// ```
237 fn test(&self) -> bool;
238
239 /// Converts this tester to `BoxTester`
240 ///
241 /// # Return Value
242 ///
243 /// A `BoxTester` that wraps this tester
244 ///
245 /// # Examples
246 ///
247 /// ```rust
248 /// use prism3_function::{Tester, BoxTester};
249 ///
250 /// let closure = || true;
251 /// let boxed: BoxTester = closure.into_box();
252 /// ```
253 fn into_box(self) -> BoxTester
254 where
255 Self: Sized + 'static,
256 {
257 BoxTester {
258 function: Box::new(move || self.test()),
259 }
260 }
261
262 /// Converts this tester to `RcTester`
263 ///
264 /// # Return Value
265 ///
266 /// A `RcTester` that wraps this tester
267 ///
268 /// # Examples
269 ///
270 /// ```rust
271 /// use prism3_function::{Tester, RcTester};
272 ///
273 /// let closure = || true;
274 /// let rc: RcTester = closure.into_rc();
275 /// ```
276 fn into_rc(self) -> RcTester
277 where
278 Self: Sized + 'static,
279 {
280 RcTester {
281 function: Rc::new(move || self.test()),
282 }
283 }
284
285 /// Converts this tester to `ArcTester`
286 ///
287 /// # Return Value
288 ///
289 /// An `ArcTester` that wraps this tester
290 ///
291 /// # Examples
292 ///
293 /// ```rust
294 /// use prism3_function::{Tester, ArcTester};
295 ///
296 /// let closure = || true;
297 /// let arc: ArcTester = closure.into_arc();
298 /// ```
299 fn into_arc(self) -> ArcTester
300 where
301 Self: Sized + Send + Sync + 'static,
302 {
303 ArcTester {
304 function: Arc::new(move || self.test()),
305 }
306 }
307
308 /// Converts this tester to a boxed function pointer
309 ///
310 /// # Return Value
311 ///
312 /// A `Box<dyn Fn() -> bool>` that wraps this tester
313 ///
314 /// # Examples
315 ///
316 /// ```rust
317 /// use prism3_function::Tester;
318 ///
319 /// let closure = || true;
320 /// let func = closure.into_fn();
321 /// assert!(func());
322 /// ```
323 fn into_fn(self) -> impl Fn() -> bool
324 where
325 Self: Sized + 'static,
326 {
327 Box::new(move || self.test())
328 }
329
330 /// Clones and converts this tester to `BoxTester`
331 ///
332 /// # Return Value
333 ///
334 /// A `BoxTester` that wraps a clone of this tester
335 ///
336 /// # Examples
337 ///
338 /// ```rust
339 /// use prism3_function::{Tester, BoxTester, ArcTester};
340 ///
341 /// let arc = ArcTester::new(|| true);
342 /// let boxed: BoxTester = arc.to_box();
343 /// // arc is still available
344 /// ```
345 fn to_box(&self) -> BoxTester
346 where
347 Self: Clone + 'static,
348 {
349 self.clone().into_box()
350 }
351
352 /// Clones and converts this tester to `RcTester`
353 ///
354 /// # Return Value
355 ///
356 /// A `RcTester` that wraps a clone of this tester
357 ///
358 /// # Examples
359 ///
360 /// ```rust
361 /// use prism3_function::{Tester, RcTester, ArcTester};
362 ///
363 /// let arc = ArcTester::new(|| true);
364 /// let rc: RcTester = arc.to_rc();
365 /// // arc is still available
366 /// ```
367 fn to_rc(&self) -> RcTester
368 where
369 Self: Clone + 'static,
370 {
371 self.clone().into_rc()
372 }
373
374 /// Clones and converts this tester to `ArcTester`
375 ///
376 /// # Return Value
377 ///
378 /// An `ArcTester` that wraps a clone of this tester
379 ///
380 /// # Examples
381 ///
382 /// ```rust
383 /// use prism3_function::{Tester, ArcTester, RcTester};
384 ///
385 /// let rc = RcTester::new(|| true);
386 /// // Note: This will panic for RcTester as it's not Send + Sync
387 /// // let arc: ArcTester = rc.to_arc();
388 /// ```
389 fn to_arc(&self) -> ArcTester
390 where
391 Self: Clone + Send + Sync + 'static,
392 {
393 self.clone().into_arc()
394 }
395
396 /// Clones and converts this tester to a boxed function pointer
397 ///
398 /// # Return Value
399 ///
400 /// A `Box<dyn Fn() -> bool>` that wraps a clone of this tester
401 ///
402 /// # Examples
403 ///
404 /// ```rust
405 /// use prism3_function::{Tester, ArcTester};
406 ///
407 /// let arc = ArcTester::new(|| true);
408 /// let func = arc.to_fn();
409 /// // arc is still available
410 /// assert!(func());
411 /// ```
412 fn to_fn(&self) -> impl Fn() -> bool
413 where
414 Self: Clone + 'static,
415 {
416 self.clone().into_fn()
417 }
418}
419
420// ============================================================================
421// BoxTester: Single Ownership Implementation
422// ============================================================================
423
424/// Single ownership Tester implemented using `Box`
425///
426/// `BoxTester` wraps a closure in `Box<dyn Fn() -> bool>`, providing single
427/// ownership semantics with no additional allocation overhead beyond the
428/// initial boxing.
429///
430/// # Characteristics
431///
432/// - **Single ownership**: Cannot be cloned
433/// - **Zero overhead**: Single heap allocation
434/// - **Consuming combination**: `and()`/`or()`/`not()` consume `self`
435/// - **Type flexibility**: Accepts any `Tester` implementation
436///
437/// # Use Cases
438///
439/// - One-time testing scenarios
440/// - Builder patterns requiring ownership transfer
441/// - Simple state checking without sharing
442/// - Chained calls with ownership transfer
443///
444/// # Examples
445///
446/// ```rust
447/// use prism3_function::{BoxTester, Tester};
448/// use std::sync::{Arc, atomic::{AtomicUsize, Ordering}};
449///
450/// // State managed externally
451/// let count = Arc::new(AtomicUsize::new(0));
452/// let count_clone = Arc::clone(&count);
453///
454/// let tester = BoxTester::new(move || {
455/// count_clone.load(Ordering::Relaxed) < 3
456/// });
457///
458/// assert!(tester.test());
459/// count.fetch_add(1, Ordering::Relaxed);
460/// assert!(tester.test());
461/// count.fetch_add(1, Ordering::Relaxed);
462/// assert!(tester.test());
463/// count.fetch_add(1, Ordering::Relaxed);
464/// assert!(!tester.test());
465///
466/// // Logical combination
467/// let combined = BoxTester::new(|| true)
468/// .and(|| false)
469/// .or(|| true);
470/// assert!(combined.test());
471/// ```
472///
473/// # Author
474///
475/// Hu Haixing
476pub struct BoxTester {
477 function: Box<dyn Fn() -> bool>,
478}
479
480impl BoxTester {
481 /// Creates a new `BoxTester` from a closure
482 ///
483 /// # Type Parameters
484 ///
485 /// * `F` - Closure type implementing `Fn() -> bool`
486 ///
487 /// # Parameters
488 ///
489 /// * `f` - The closure to wrap
490 ///
491 /// # Return Value
492 ///
493 /// A new `BoxTester` instance
494 ///
495 /// # Examples
496 ///
497 /// ```rust
498 /// use prism3_function::BoxTester;
499 ///
500 /// let tester = BoxTester::new(|| true);
501 /// ```
502 pub fn new<F>(f: F) -> Self
503 where
504 F: Fn() -> bool + 'static,
505 {
506 BoxTester {
507 function: Box::new(f),
508 }
509 }
510
511 /// Combines this tester with another tester using logical AND
512 ///
513 /// Returns a new `BoxTester` that returns `true` only when both tests
514 /// pass. Short-circuit evaluation: if the first test fails, the second
515 /// will not be executed.
516 ///
517 /// # Type Parameters
518 ///
519 /// * `T` - Type implementing `Tester`
520 ///
521 /// # Parameters
522 ///
523 /// * `next` - The tester to combine with
524 ///
525 /// # Return Value
526 ///
527 /// A new `BoxTester` representing logical AND
528 ///
529 /// # Examples
530 ///
531 /// ```rust
532 /// use prism3_function::{BoxTester, Tester};
533 /// use std::sync::{Arc, atomic::{AtomicUsize, AtomicBool, Ordering}};
534 ///
535 /// // Simulate service status
536 /// let request_count = Arc::new(AtomicUsize::new(0));
537 /// let is_available = Arc::new(AtomicBool::new(true));
538 /// let max_requests = 1000;
539 ///
540 /// let count_clone = Arc::clone(&request_count);
541 /// let available_clone = Arc::clone(&is_available);
542 ///
543 /// // Service available and request count not exceeded
544 /// let service_ok = BoxTester::new(move || {
545 /// available_clone.load(Ordering::Relaxed)
546 /// })
547 /// .and(move || {
548 /// count_clone.load(Ordering::Relaxed) < max_requests
549 /// });
550 ///
551 /// // Initial state: available and request count 0
552 /// assert!(service_ok.test());
553 ///
554 /// // Simulate request increase
555 /// request_count.store(500, Ordering::Relaxed);
556 /// assert!(service_ok.test());
557 ///
558 /// // Request count exceeded
559 /// request_count.store(1500, Ordering::Relaxed);
560 /// assert!(!service_ok.test());
561 ///
562 /// // Service unavailable
563 /// is_available.store(false, Ordering::Relaxed);
564 /// assert!(!service_ok.test());
565 /// ```
566 pub fn and<T>(self, next: T) -> BoxTester
567 where
568 T: Tester + 'static,
569 {
570 let self_fn = self.function;
571 let next_tester = next;
572 BoxTester::new(move || self_fn() && next_tester.test())
573 }
574
575 /// Combines this tester with another tester using logical OR
576 ///
577 /// Returns a new `BoxTester` that returns `true` if either test passes.
578 /// Short-circuit evaluation: if the first test passes, the second will
579 /// not be executed.
580 ///
581 /// # Type Parameters
582 ///
583 /// * `T` - Type implementing `Tester`
584 ///
585 /// # Parameters
586 ///
587 /// * `next` - The tester to combine with
588 ///
589 /// # Return Value
590 ///
591 /// A new `BoxTester` representing logical OR
592 ///
593 /// # Examples
594 ///
595 /// ```rust
596 /// use prism3_function::{BoxTester, Tester};
597 /// use std::sync::{Arc, atomic::{AtomicUsize, AtomicBool, Ordering}};
598 ///
599 /// // Simulate service status
600 /// let request_count = Arc::new(AtomicUsize::new(0));
601 /// let is_healthy = Arc::new(AtomicBool::new(true));
602 /// let max_requests = 100;
603 ///
604 /// let count_clone = Arc::clone(&request_count);
605 /// let health_clone = Arc::clone(&is_healthy);
606 ///
607 /// // Service healthy or low request count
608 /// let can_serve = BoxTester::new(move || {
609 /// health_clone.load(Ordering::Relaxed)
610 /// })
611 /// .or(move || {
612 /// count_clone.load(Ordering::Relaxed) < max_requests
613 /// });
614 ///
615 /// // Initial state: healthy and request count 0
616 /// assert!(can_serve.test());
617 ///
618 /// // Request count increased but within limit
619 /// request_count.store(50, Ordering::Relaxed);
620 /// assert!(can_serve.test());
621 ///
622 /// // Request count exceeded but service healthy
623 /// request_count.store(150, Ordering::Relaxed);
624 /// assert!(can_serve.test()); // still healthy
625 ///
626 /// // Service unhealthy but low request count
627 /// is_healthy.store(false, Ordering::Relaxed);
628 /// request_count.store(50, Ordering::Relaxed);
629 /// assert!(can_serve.test()); // low request count
630 ///
631 /// // Unhealthy and high request count
632 /// request_count.store(150, Ordering::Relaxed);
633 /// assert!(!can_serve.test());
634 /// ```
635 pub fn or<T>(self, next: T) -> BoxTester
636 where
637 T: Tester + 'static,
638 {
639 let self_fn = self.function;
640 let next_tester = next;
641 BoxTester::new(move || self_fn() || next_tester.test())
642 }
643
644 /// Negates the result of this tester
645 ///
646 /// Returns a new `BoxTester` that returns the opposite value of the
647 /// original test result.
648 ///
649 /// # Return Value
650 ///
651 /// A new `BoxTester` representing logical NOT
652 ///
653 /// # Examples
654 ///
655 /// ```rust
656 /// use prism3_function::{BoxTester, Tester};
657 /// use std::sync::{Arc, atomic::{AtomicUsize, Ordering}};
658 ///
659 /// // Simulate resource usage
660 /// let memory_usage = Arc::new(AtomicUsize::new(0));
661 /// let max_memory = 1024; // MB
662 ///
663 /// let memory_clone = Arc::clone(&memory_usage);
664 ///
665 /// // Memory usage not exceeded
666 /// let memory_ok = BoxTester::new(move || {
667 /// memory_clone.load(Ordering::Relaxed) <= max_memory
668 /// });
669 ///
670 /// // Initial state: normal memory usage
671 /// memory_usage.store(512, Ordering::Relaxed);
672 /// assert!(memory_ok.test());
673 ///
674 /// // Memory usage exceeded (negated)
675 /// let memory_critical = memory_ok.not();
676 /// assert!(!memory_critical.test());
677 ///
678 /// // Memory usage exceeded
679 /// memory_usage.store(2048, Ordering::Relaxed);
680 /// assert!(memory_critical.test());
681 /// ```
682 #[allow(clippy::should_implement_trait)]
683 pub fn not(self) -> BoxTester {
684 let self_fn = self.function;
685 BoxTester::new(move || !self_fn())
686 }
687
688 /// Combines this tester with another tester using logical NAND
689 ///
690 /// Returns a new `BoxTester` that returns `true` unless both tests pass.
691 /// Equivalent to `!(self AND other)`.
692 ///
693 /// # Type Parameters
694 ///
695 /// * `T` - Type implementing `Tester`
696 ///
697 /// # Parameters
698 ///
699 /// * `next` - The tester to combine with
700 ///
701 /// # Return Value
702 ///
703 /// A new `BoxTester` representing logical NAND
704 ///
705 /// # Examples
706 ///
707 /// ```rust
708 /// use prism3_function::{BoxTester, Tester};
709 /// use std::sync::{Arc, atomic::{AtomicBool, Ordering}};
710 ///
711 /// let flag1 = Arc::new(AtomicBool::new(true));
712 /// let flag2 = Arc::new(AtomicBool::new(true));
713 ///
714 /// let flag1_clone = Arc::clone(&flag1);
715 /// let flag2_clone = Arc::clone(&flag2);
716 ///
717 /// let nand = BoxTester::new(move || {
718 /// flag1_clone.load(Ordering::Relaxed)
719 /// })
720 /// .nand(move || {
721 /// flag2_clone.load(Ordering::Relaxed)
722 /// });
723 ///
724 /// // Both true returns false
725 /// assert!(!nand.test());
726 ///
727 /// // At least one false returns true
728 /// flag1.store(false, Ordering::Relaxed);
729 /// assert!(nand.test());
730 /// ```
731 pub fn nand<T>(self, next: T) -> BoxTester
732 where
733 T: Tester + 'static,
734 {
735 let self_fn = self.function;
736 let next_tester = next;
737 BoxTester::new(move || !(self_fn() && next_tester.test()))
738 }
739
740 /// Combines this tester with another tester using logical XOR
741 ///
742 /// Returns a new `BoxTester` that returns `true` if exactly one test
743 /// passes.
744 ///
745 /// # Type Parameters
746 ///
747 /// * `T` - Type implementing `Tester`
748 ///
749 /// # Parameters
750 ///
751 /// * `next` - The tester to combine with
752 ///
753 /// # Return Value
754 ///
755 /// A new `BoxTester` representing logical XOR
756 ///
757 /// # Examples
758 ///
759 /// ```rust
760 /// use prism3_function::{BoxTester, Tester};
761 /// use std::sync::{Arc, atomic::{AtomicBool, Ordering}};
762 ///
763 /// let flag1 = Arc::new(AtomicBool::new(true));
764 /// let flag2 = Arc::new(AtomicBool::new(false));
765 ///
766 /// let flag1_clone1 = Arc::clone(&flag1);
767 /// let flag2_clone1 = Arc::clone(&flag2);
768 ///
769 /// let xor = BoxTester::new(move || {
770 /// flag1_clone1.load(Ordering::Relaxed)
771 /// })
772 /// .xor(move || {
773 /// flag2_clone1.load(Ordering::Relaxed)
774 /// });
775 ///
776 /// // One true one false returns true
777 /// assert!(xor.test());
778 ///
779 /// // Both true returns false
780 /// flag2.store(true, Ordering::Relaxed);
781 /// assert!(!xor.test());
782 ///
783 /// // Both false returns false
784 /// flag1.store(false, Ordering::Relaxed);
785 /// flag2.store(false, Ordering::Relaxed);
786 /// assert!(!xor.test());
787 /// ```
788 pub fn xor<T>(self, next: T) -> BoxTester
789 where
790 T: Tester + 'static,
791 {
792 let self_fn = self.function;
793 let next_tester = next;
794 BoxTester::new(move || self_fn() ^ next_tester.test())
795 }
796
797 /// Combines this tester with another tester using logical NOR
798 ///
799 /// Returns a new `BoxTester` that returns `true` only when both tests
800 /// fail. Equivalent to `!(self OR other)`.
801 ///
802 /// # Type Parameters
803 ///
804 /// * `T` - Type implementing `Tester`
805 ///
806 /// # Parameters
807 ///
808 /// * `next` - The tester to combine with
809 ///
810 /// # Return Value
811 ///
812 /// A new `BoxTester` representing logical NOR
813 ///
814 /// # Examples
815 ///
816 /// ```rust
817 /// use prism3_function::{BoxTester, Tester};
818 /// use std::sync::{Arc, atomic::{AtomicBool, Ordering}};
819 ///
820 /// let flag1 = Arc::new(AtomicBool::new(false));
821 /// let flag2 = Arc::new(AtomicBool::new(false));
822 ///
823 /// let flag1_clone = Arc::clone(&flag1);
824 /// let flag2_clone = Arc::clone(&flag2);
825 ///
826 /// let nor = BoxTester::new(move || {
827 /// flag1_clone.load(Ordering::Relaxed)
828 /// })
829 /// .nor(move || {
830 /// flag2_clone.load(Ordering::Relaxed)
831 /// });
832 ///
833 /// // Both false returns true
834 /// assert!(nor.test());
835 ///
836 /// // At least one true returns false
837 /// flag1.store(true, Ordering::Relaxed);
838 /// assert!(!nor.test());
839 /// ```
840 pub fn nor<T>(self, next: T) -> BoxTester
841 where
842 T: Tester + 'static,
843 {
844 let self_fn = self.function;
845 let next_tester = next;
846 BoxTester::new(move || !(self_fn() || next_tester.test()))
847 }
848}
849
850impl Tester for BoxTester {
851 fn test(&self) -> bool {
852 (self.function)()
853 }
854
855 fn into_box(self) -> BoxTester {
856 self
857 }
858
859 fn into_rc(self) -> RcTester {
860 let func = self.function;
861 RcTester {
862 function: Rc::new(func),
863 }
864 }
865
866 // Note: BoxTester does not implement Send + Sync, so into_arc()
867 // cannot be implemented. Calling into_arc() on BoxTester will result
868 // in a compile error due to the Send + Sync trait bounds not being
869 // satisfied. The default Tester trait implementation will be used.
870
871 fn into_fn(self) -> impl Fn() -> bool {
872 self.function
873 }
874
875 // Note: BoxTester does not implement Clone, so to_box(), to_rc(),
876 // to_arc(), and to_fn() cannot be implemented. Calling these methods
877 // on BoxTester will result in a compile error due to the Clone trait
878 // bound not being satisfied. The default Tester trait implementations
879 // will be used.
880}
881
882// ============================================================================
883// ArcTester: Thread-Safe Shared Ownership Implementation
884// ============================================================================
885
886/// Thread-safe shared ownership Tester implemented using `Arc`
887///
888/// `ArcTester` wraps a closure in `Arc<dyn Fn() -> bool + Send + Sync>`,
889/// allowing the tester to be cloned and safely shared across threads.
890///
891/// # Characteristics
892///
893/// - **Shared ownership**: Can be cloned
894/// - **Thread-safe**: Can be sent across threads
895/// - **Lock-free overhead**: Uses `Fn` without needing `Mutex`
896/// - **Borrowing combination**: `and()`/`or()`/`not()` borrow `&self`
897///
898/// # Use Cases
899///
900/// - Multi-threaded testing scenarios
901/// - Health checks shared across threads
902/// - Test states requiring concurrent access
903/// - Background monitoring tasks
904///
905/// # Examples
906///
907/// ```rust
908/// use prism3_function::{ArcTester, Tester};
909/// use std::sync::{Arc, atomic::{AtomicUsize, Ordering}};
910/// use std::thread;
911///
912/// // Shared atomic counter
913/// let counter = Arc::new(AtomicUsize::new(0));
914/// let counter_clone = Arc::clone(&counter);
915///
916/// let shared = ArcTester::new(move || {
917/// counter_clone.load(Ordering::Relaxed) <= 5
918/// });
919///
920/// let clone = shared.clone();
921/// let handle = thread::spawn(move || {
922/// clone.test()
923/// });
924///
925/// assert!(handle.join().unwrap());
926/// counter.fetch_add(1, Ordering::Relaxed);
927/// assert!(shared.test());
928/// ```
929///
930/// # Author
931///
932/// Hu Haixing
933pub struct ArcTester {
934 function: Arc<dyn Fn() -> bool + Send + Sync>,
935}
936
937impl ArcTester {
938 /// Creates a new `ArcTester` from a closure
939 ///
940 /// # Type Parameters
941 ///
942 /// * `F` - Closure type implementing `Fn() -> bool + Send + Sync`
943 ///
944 /// # Parameters
945 ///
946 /// * `f` - The closure to wrap
947 ///
948 /// # Return Value
949 ///
950 /// A new `ArcTester` instance
951 ///
952 /// # Examples
953 ///
954 /// ```rust
955 /// use prism3_function::ArcTester;
956 ///
957 /// let tester = ArcTester::new(|| true);
958 /// ```
959 pub fn new<F>(f: F) -> Self
960 where
961 F: Fn() -> bool + Send + Sync + 'static,
962 {
963 ArcTester {
964 function: Arc::new(f),
965 }
966 }
967
968 /// Combines this tester with another tester using logical AND
969 ///
970 /// Returns a new `ArcTester` that returns `true` only when both tests
971 /// pass. Borrows `&self`, so the original tester remains available.
972 ///
973 /// # Parameters
974 ///
975 /// * `next` - The tester to combine with
976 ///
977 /// # Return Value
978 ///
979 /// A new `ArcTester` representing logical AND
980 ///
981 /// # Examples
982 ///
983 /// ```rust
984 /// use prism3_function::{ArcTester, Tester};
985 /// use std::sync::{Arc, atomic::{AtomicUsize, AtomicBool, Ordering}};
986 /// use std::thread;
987 ///
988 /// // Simulate database connection pool status
989 /// let active_connections = Arc::new(AtomicUsize::new(0));
990 /// let is_pool_healthy = Arc::new(AtomicBool::new(true));
991 /// let max_connections = 50;
992 ///
993 /// let conn_clone = Arc::clone(&active_connections);
994 /// let health_clone = Arc::clone(&is_pool_healthy);
995 ///
996 /// // Connection pool health check
997 /// let pool_healthy = ArcTester::new(move || {
998 /// health_clone.load(Ordering::Relaxed)
999 /// });
1000 ///
1001 /// // Connection count check
1002 /// let conn_ok = ArcTester::new(move || {
1003 /// conn_clone.load(Ordering::Relaxed) < max_connections
1004 /// });
1005 ///
1006 /// // Combined check: pool healthy and connection count not exceeded
1007 /// let pool_ready = pool_healthy.and(&conn_ok);
1008 ///
1009 /// // Multi-threaded test
1010 /// let pool_ready_clone = pool_ready.clone();
1011 /// let handle = thread::spawn(move || {
1012 /// pool_ready_clone.test()
1013 /// });
1014 ///
1015 /// // Initial state should pass
1016 /// assert!(handle.join().unwrap());
1017 /// assert!(pool_ready.test());
1018 ///
1019 /// // Connection count exceeded
1020 /// active_connections.store(60, Ordering::Relaxed);
1021 /// assert!(!pool_ready.test());
1022 ///
1023 /// // Connection pool unhealthy
1024 /// is_pool_healthy.store(false, Ordering::Relaxed);
1025 /// assert!(!pool_ready.test());
1026 /// ```
1027 pub fn and(&self, next: &ArcTester) -> ArcTester {
1028 let self_fn = Arc::clone(&self.function);
1029 let next_fn = Arc::clone(&next.function);
1030 ArcTester {
1031 function: Arc::new(move || self_fn() && next_fn()),
1032 }
1033 }
1034
1035 /// Combines this tester with another tester using logical OR
1036 ///
1037 /// Returns a new `ArcTester` that returns `true` if either test passes.
1038 /// Borrows `&self`, so the original tester remains available.
1039 ///
1040 /// # Parameters
1041 ///
1042 /// * `next` - The tester to combine with
1043 ///
1044 /// # Return Value
1045 ///
1046 /// A new `ArcTester` representing logical OR
1047 ///
1048 /// # Examples
1049 ///
1050 /// ```rust
1051 /// use prism3_function::{ArcTester, Tester};
1052 /// use std::sync::{Arc, atomic::{AtomicUsize, AtomicBool, Ordering}};
1053 /// use std::thread;
1054 ///
1055 /// // Simulate load balancer status
1056 /// let server_load = Arc::new(AtomicUsize::new(0));
1057 /// let is_server_healthy = Arc::new(AtomicBool::new(true));
1058 /// let max_load = 80;
1059 /// let emergency_mode = Arc::new(AtomicBool::new(false));
1060 ///
1061 /// let load_clone = Arc::clone(&server_load);
1062 /// let health_clone = Arc::clone(&is_server_healthy);
1063 /// let emergency_clone = Arc::clone(&emergency_mode);
1064 ///
1065 /// // Server low load
1066 /// let low_load = ArcTester::new(move || {
1067 /// load_clone.load(Ordering::Relaxed) < max_load
1068 /// });
1069 ///
1070 /// // Emergency mode check
1071 /// let emergency_check = ArcTester::new(move || {
1072 /// emergency_clone.load(Ordering::Relaxed)
1073 /// });
1074 ///
1075 /// // Server health check
1076 /// let server_healthy = ArcTester::new(move || {
1077 /// health_clone.load(Ordering::Relaxed)
1078 /// });
1079 ///
1080 /// // Emergency mode or server healthy
1081 /// let can_handle_requests = emergency_check.or(&server_healthy);
1082 ///
1083 /// // Combined condition: low load or can handle requests
1084 /// let should_route_here = low_load.or(&can_handle_requests);
1085 ///
1086 /// // Multi-threaded test
1087 /// let router_clone = should_route_here.clone();
1088 /// let handle = thread::spawn(move || {
1089 /// router_clone.test()
1090 /// });
1091 ///
1092 /// // Initial state: low load and healthy
1093 /// assert!(handle.join().unwrap());
1094 /// assert!(should_route_here.test());
1095 ///
1096 /// // High load but server healthy
1097 /// server_load.store(90, Ordering::Relaxed);
1098 /// assert!(should_route_here.test()); // still healthy
1099 ///
1100 /// // Server unhealthy but emergency mode
1101 /// is_server_healthy.store(false, Ordering::Relaxed);
1102 /// emergency_mode.store(true, Ordering::Relaxed);
1103 /// assert!(should_route_here.test()); // emergency mode
1104 ///
1105 /// // Unhealthy and not emergency mode
1106 /// emergency_mode.store(false, Ordering::Relaxed);
1107 /// assert!(!should_route_here.test());
1108 /// ```
1109 pub fn or(&self, next: &ArcTester) -> ArcTester {
1110 let self_fn = Arc::clone(&self.function);
1111 let next_fn = Arc::clone(&next.function);
1112 ArcTester {
1113 function: Arc::new(move || self_fn() || next_fn()),
1114 }
1115 }
1116
1117 /// Negates the result of this tester
1118 ///
1119 /// Returns a new `ArcTester` that returns the opposite value of the
1120 /// original test result. Borrows `&self`, so the original tester remains
1121 /// available.
1122 ///
1123 /// # Return Value
1124 ///
1125 /// A new `ArcTester` representing logical NOT
1126 ///
1127 /// # Examples
1128 ///
1129 /// ```rust
1130 /// use prism3_function::{ArcTester, Tester};
1131 /// use std::sync::{Arc, atomic::{AtomicUsize, Ordering}};
1132 /// use std::thread;
1133 ///
1134 /// // Simulate task queue status
1135 /// let pending_tasks = Arc::new(AtomicUsize::new(0));
1136 /// let max_queue_size = 100;
1137 ///
1138 /// let tasks_clone = Arc::clone(&pending_tasks);
1139 ///
1140 /// // Queue not full
1141 /// let queue_available = ArcTester::new(move || {
1142 /// tasks_clone.load(Ordering::Relaxed) < max_queue_size
1143 /// });
1144 ///
1145 /// // Queue full (negated)
1146 /// let queue_full = queue_available.not();
1147 ///
1148 /// // Multi-threaded test
1149 /// let queue_full_clone = queue_full.clone();
1150 /// let handle = thread::spawn(move || {
1151 /// queue_full_clone.test()
1152 /// });
1153 ///
1154 /// // Initial state: queue not full
1155 /// pending_tasks.store(50, Ordering::Relaxed);
1156 /// assert!(queue_available.test());
1157 /// assert!(!handle.join().unwrap());
1158 /// assert!(!queue_full.test());
1159 ///
1160 /// // Queue near full
1161 /// pending_tasks.store(95, Ordering::Relaxed);
1162 /// assert!(queue_available.test());
1163 /// assert!(!queue_full.test());
1164 ///
1165 /// // Queue full
1166 /// pending_tasks.store(120, Ordering::Relaxed);
1167 /// assert!(!queue_available.test());
1168 /// assert!(queue_full.test());
1169 /// ```
1170 #[allow(clippy::should_implement_trait)]
1171 pub fn not(&self) -> ArcTester {
1172 let func = Arc::clone(&self.function);
1173 ArcTester {
1174 function: Arc::new(move || !func()),
1175 }
1176 }
1177
1178 /// Combines this tester with another tester using logical NAND
1179 ///
1180 /// Returns a new `ArcTester` that returns `true` unless both tests pass.
1181 /// Borrows `&self`, so the original tester remains available.
1182 ///
1183 /// # Parameters
1184 ///
1185 /// * `next` - The tester to combine with
1186 ///
1187 /// # Return Value
1188 ///
1189 /// A new `ArcTester` representing logical NAND
1190 ///
1191 /// # Examples
1192 ///
1193 /// ```rust
1194 /// use prism3_function::{ArcTester, Tester};
1195 /// use std::sync::{Arc, atomic::{AtomicBool, Ordering}};
1196 /// use std::thread;
1197 ///
1198 /// let flag1 = Arc::new(AtomicBool::new(true));
1199 /// let flag2 = Arc::new(AtomicBool::new(true));
1200 ///
1201 /// let flag1_clone = Arc::clone(&flag1);
1202 /// let flag2_clone = Arc::clone(&flag2);
1203 ///
1204 /// let tester1 = ArcTester::new(move || {
1205 /// flag1_clone.load(Ordering::Relaxed)
1206 /// });
1207 ///
1208 /// let tester2 = ArcTester::new(move || {
1209 /// flag2_clone.load(Ordering::Relaxed)
1210 /// });
1211 ///
1212 /// let nand = tester1.nand(&tester2);
1213 ///
1214 /// // Both true returns false
1215 /// assert!(!nand.test());
1216 ///
1217 /// // At least one false returns true
1218 /// flag1.store(false, Ordering::Relaxed);
1219 /// assert!(nand.test());
1220 ///
1221 /// // Original tester still available
1222 /// assert!(!tester1.test());
1223 /// assert!(tester2.test());
1224 /// ```
1225 pub fn nand(&self, next: &ArcTester) -> ArcTester {
1226 let self_fn = Arc::clone(&self.function);
1227 let next_fn = Arc::clone(&next.function);
1228 ArcTester {
1229 function: Arc::new(move || !(self_fn() && next_fn())),
1230 }
1231 }
1232
1233 /// Combines this tester with another tester using logical XOR
1234 ///
1235 /// Returns a new `ArcTester` that returns `true` if exactly one test
1236 /// passes. Borrows `&self`, so the original tester remains available.
1237 ///
1238 /// # Parameters
1239 ///
1240 /// * `next` - The tester to combine with
1241 ///
1242 /// # Return Value
1243 ///
1244 /// A new `ArcTester` representing logical XOR
1245 ///
1246 /// # Examples
1247 ///
1248 /// ```rust
1249 /// use prism3_function::{ArcTester, Tester};
1250 /// use std::sync::{Arc, atomic::{AtomicBool, Ordering}};
1251 /// use std::thread;
1252 ///
1253 /// let flag1 = Arc::new(AtomicBool::new(true));
1254 /// let flag2 = Arc::new(AtomicBool::new(false));
1255 ///
1256 /// let flag1_clone = Arc::clone(&flag1);
1257 /// let flag2_clone = Arc::clone(&flag2);
1258 ///
1259 /// let tester1 = ArcTester::new(move || {
1260 /// flag1_clone.load(Ordering::Relaxed)
1261 /// });
1262 ///
1263 /// let tester2 = ArcTester::new(move || {
1264 /// flag2_clone.load(Ordering::Relaxed)
1265 /// });
1266 ///
1267 /// let xor = tester1.xor(&tester2);
1268 ///
1269 /// // One true one false returns true
1270 /// assert!(xor.test());
1271 ///
1272 /// // Both true returns false
1273 /// flag2.store(true, Ordering::Relaxed);
1274 /// assert!(!xor.test());
1275 ///
1276 /// // Both false returns false
1277 /// flag1.store(false, Ordering::Relaxed);
1278 /// flag2.store(false, Ordering::Relaxed);
1279 /// assert!(!xor.test());
1280 ///
1281 /// // Original tester still available
1282 /// assert!(!tester1.test());
1283 /// assert!(!tester2.test());
1284 /// ```
1285 pub fn xor(&self, next: &ArcTester) -> ArcTester {
1286 let self_fn = Arc::clone(&self.function);
1287 let next_fn = Arc::clone(&next.function);
1288 ArcTester {
1289 function: Arc::new(move || self_fn() ^ next_fn()),
1290 }
1291 }
1292
1293 /// Combines this tester with another tester using logical NOR
1294 ///
1295 /// Returns a new `ArcTester` that returns `true` only when both tests
1296 /// fail. Borrows `&self`, so the original tester remains available.
1297 ///
1298 /// # Parameters
1299 ///
1300 /// * `next` - The tester to combine with
1301 ///
1302 /// # Return Value
1303 ///
1304 /// A new `ArcTester` representing logical NOR
1305 ///
1306 /// # Examples
1307 ///
1308 /// ```rust
1309 /// use prism3_function::{ArcTester, Tester};
1310 /// use std::sync::{Arc, atomic::{AtomicBool, Ordering}};
1311 /// use std::thread;
1312 ///
1313 /// let flag1 = Arc::new(AtomicBool::new(false));
1314 /// let flag2 = Arc::new(AtomicBool::new(false));
1315 ///
1316 /// let flag1_clone = Arc::clone(&flag1);
1317 /// let flag2_clone = Arc::clone(&flag2);
1318 ///
1319 /// let tester1 = ArcTester::new(move || {
1320 /// flag1_clone.load(Ordering::Relaxed)
1321 /// });
1322 ///
1323 /// let tester2 = ArcTester::new(move || {
1324 /// flag2_clone.load(Ordering::Relaxed)
1325 /// });
1326 ///
1327 /// let nor = tester1.nor(&tester2);
1328 ///
1329 /// // Both false returns true
1330 /// assert!(nor.test());
1331 ///
1332 /// // At least one true returns false
1333 /// flag1.store(true, Ordering::Relaxed);
1334 /// assert!(!nor.test());
1335 ///
1336 /// // Original tester still available
1337 /// assert!(tester1.test());
1338 /// assert!(!tester2.test());
1339 /// ```
1340 pub fn nor(&self, next: &ArcTester) -> ArcTester {
1341 let self_fn = Arc::clone(&self.function);
1342 let next_fn = Arc::clone(&next.function);
1343 ArcTester {
1344 function: Arc::new(move || !(self_fn() || next_fn())),
1345 }
1346 }
1347}
1348
1349impl Tester for ArcTester {
1350 fn test(&self) -> bool {
1351 (self.function)()
1352 }
1353
1354 fn into_box(self) -> BoxTester {
1355 let func = self.function;
1356 BoxTester {
1357 function: Box::new(move || func()),
1358 }
1359 }
1360
1361 fn into_rc(self) -> RcTester {
1362 let func = self.function;
1363 RcTester {
1364 function: Rc::new(move || func()),
1365 }
1366 }
1367
1368 fn into_arc(self) -> ArcTester {
1369 self
1370 }
1371
1372 fn into_fn(self) -> impl Fn() -> bool {
1373 move || (self.function)()
1374 }
1375
1376 fn to_box(&self) -> BoxTester {
1377 let self_fn = self.function.clone();
1378 BoxTester {
1379 function: Box::new(move || self_fn()),
1380 }
1381 }
1382
1383 fn to_rc(&self) -> RcTester {
1384 let self_fn = self.function.clone();
1385 RcTester {
1386 function: Rc::new(move || self_fn()),
1387 }
1388 }
1389
1390 fn to_arc(&self) -> ArcTester {
1391 self.clone()
1392 }
1393
1394 fn to_fn(&self) -> impl Fn() -> bool {
1395 let self_fn = self.function.clone();
1396 move || self_fn()
1397 }
1398}
1399
1400impl Clone for ArcTester {
1401 /// Creates a clone of this `ArcTester`.
1402 ///
1403 /// The cloned instance shares the same underlying function with
1404 /// the original, allowing multiple references to the same test
1405 /// logic.
1406 fn clone(&self) -> Self {
1407 Self {
1408 function: Arc::clone(&self.function),
1409 }
1410 }
1411}
1412
1413// ============================================================================
1414// RcTester: Single-Threaded Shared Ownership Implementation
1415// ============================================================================
1416
1417/// Single-threaded shared ownership Tester implemented using `Rc`
1418///
1419/// `RcTester` wraps a closure in `Rc<dyn Fn() -> bool>`, allowing the tester
1420/// to be cloned and shared within a single thread. Since it doesn't use atomic
1421/// operations, it has lower overhead than `ArcTester`.
1422///
1423/// # Characteristics
1424///
1425/// - **Shared ownership**: Can be cloned
1426/// - **Single-threaded**: Cannot be sent across threads
1427/// - **Low overhead**: Uses `Fn` without needing `RefCell`
1428/// - **Borrowing combination**: `and()`/`or()`/`not()` borrow `&self`
1429///
1430/// # Use Cases
1431///
1432/// - Single-threaded testing scenarios requiring sharing
1433/// - Event-driven systems (single-threaded)
1434/// - Callback-intensive code requiring cloneable tests
1435/// - Performance-sensitive single-threaded code
1436///
1437/// # Examples
1438///
1439/// ```rust
1440/// use prism3_function::{RcTester, Tester};
1441///
1442/// let shared = RcTester::new(|| true);
1443///
1444/// // Clone for multiple uses
1445/// let clone1 = shared.clone();
1446/// let clone2 = shared.clone();
1447///
1448/// // Non-consuming combination
1449/// let combined = shared.and(&clone1);
1450/// ```
1451///
1452/// # Author
1453///
1454/// Hu Haixing
1455pub struct RcTester {
1456 function: Rc<dyn Fn() -> bool>,
1457}
1458
1459impl RcTester {
1460 /// Creates a new `RcTester` from a closure
1461 ///
1462 /// # Type Parameters
1463 ///
1464 /// * `F` - Closure type implementing `Fn() -> bool`
1465 ///
1466 /// # Parameters
1467 ///
1468 /// * `f` - The closure to wrap
1469 ///
1470 /// # Return Value
1471 ///
1472 /// A new `RcTester` instance
1473 ///
1474 /// # Examples
1475 ///
1476 /// ```rust
1477 /// use prism3_function::RcTester;
1478 ///
1479 /// let tester = RcTester::new(|| true);
1480 /// ```
1481 pub fn new<F>(f: F) -> Self
1482 where
1483 F: Fn() -> bool + 'static,
1484 {
1485 RcTester {
1486 function: Rc::new(f),
1487 }
1488 }
1489
1490 /// Combines this tester with another tester using logical AND
1491 ///
1492 /// Returns a new `RcTester` that returns `true` only when both tests
1493 /// pass. Borrows `&self`, so the original tester remains available.
1494 ///
1495 /// # Parameters
1496 ///
1497 /// * `next` - The tester to combine with
1498 ///
1499 /// # Return Value
1500 ///
1501 /// A new `RcTester` representing logical AND
1502 ///
1503 /// # Examples
1504 ///
1505 /// ```rust
1506 /// use prism3_function::{RcTester, Tester};
1507 ///
1508 /// let first = RcTester::new(|| true);
1509 /// let second = RcTester::new(|| true);
1510 /// let combined = first.and(&second);
1511 /// // first and second are still available
1512 /// ```
1513 pub fn and(&self, next: &RcTester) -> RcTester {
1514 let self_fn = Rc::clone(&self.function);
1515 let next_fn = Rc::clone(&next.function);
1516 RcTester {
1517 function: Rc::new(move || self_fn() && next_fn()),
1518 }
1519 }
1520
1521 /// Combines this tester with another tester using logical OR
1522 ///
1523 /// Returns a new `RcTester` that returns `true` if either test passes.
1524 /// Borrows `&self`, so the original tester remains available.
1525 ///
1526 /// # Parameters
1527 ///
1528 /// * `next` - The tester to combine with
1529 ///
1530 /// # Return Value
1531 ///
1532 /// A new `RcTester` representing logical OR
1533 ///
1534 /// # Examples
1535 ///
1536 /// ```rust
1537 /// use prism3_function::{RcTester, Tester};
1538 ///
1539 /// let first = RcTester::new(|| false);
1540 /// let second = RcTester::new(|| true);
1541 /// let combined = first.or(&second);
1542 /// // first and second are still available
1543 /// ```
1544 pub fn or(&self, next: &RcTester) -> RcTester {
1545 let self_fn = Rc::clone(&self.function);
1546 let next_fn = Rc::clone(&next.function);
1547 RcTester {
1548 function: Rc::new(move || self_fn() || next_fn()),
1549 }
1550 }
1551
1552 /// Negates the result of this tester
1553 ///
1554 /// Returns a new `RcTester` that returns the opposite value of the
1555 /// original test result. Borrows `&self`, so the original tester remains
1556 /// available.
1557 ///
1558 /// # Return Value
1559 ///
1560 /// A new `RcTester` representing logical NOT
1561 ///
1562 /// # Examples
1563 ///
1564 /// ```rust
1565 /// use prism3_function::{RcTester, Tester};
1566 ///
1567 /// let original = RcTester::new(|| true);
1568 /// let negated = original.not();
1569 /// // original is still available
1570 /// ```
1571 #[allow(clippy::should_implement_trait)]
1572 pub fn not(&self) -> RcTester {
1573 let self_fn = Rc::clone(&self.function);
1574 RcTester {
1575 function: Rc::new(move || !self_fn()),
1576 }
1577 }
1578
1579 /// Combines this tester with another tester using logical NAND
1580 ///
1581 /// Returns a new `RcTester` that returns `true` unless both tests pass.
1582 /// Borrows `&self`, so the original tester remains available.
1583 ///
1584 /// # Parameters
1585 ///
1586 /// * `next` - The tester to combine with
1587 ///
1588 /// # Return Value
1589 ///
1590 /// A new `RcTester` representing logical NAND
1591 ///
1592 /// # Examples
1593 ///
1594 /// ```rust
1595 /// use prism3_function::{RcTester, Tester};
1596 ///
1597 /// let first = RcTester::new(|| true);
1598 /// let second = RcTester::new(|| true);
1599 /// let nand = first.nand(&second);
1600 ///
1601 /// // Both true returns false
1602 /// assert!(!nand.test());
1603 ///
1604 /// // first and second still available
1605 /// assert!(first.test());
1606 /// assert!(second.test());
1607 /// ```
1608 pub fn nand(&self, next: &RcTester) -> RcTester {
1609 let self_fn = Rc::clone(&self.function);
1610 let next_fn = Rc::clone(&next.function);
1611 RcTester {
1612 function: Rc::new(move || !(self_fn() && next_fn())),
1613 }
1614 }
1615
1616 /// Combines this tester with another tester using logical XOR
1617 ///
1618 /// Returns a new `RcTester` that returns `true` if exactly one test
1619 /// passes. Borrows `&self`, so the original tester remains available.
1620 ///
1621 /// # Parameters
1622 ///
1623 /// * `next` - The tester to combine with
1624 ///
1625 /// # Return Value
1626 ///
1627 /// A new `RcTester` representing logical XOR
1628 ///
1629 /// # Examples
1630 ///
1631 /// ```rust
1632 /// use prism3_function::{RcTester, Tester};
1633 ///
1634 /// let first = RcTester::new(|| true);
1635 /// let second = RcTester::new(|| false);
1636 /// let xor = first.xor(&second);
1637 ///
1638 /// // One true one false returns true
1639 /// assert!(xor.test());
1640 ///
1641 /// // first and second still available
1642 /// assert!(first.test());
1643 /// assert!(!second.test());
1644 /// ```
1645 pub fn xor(&self, next: &RcTester) -> RcTester {
1646 let self_fn = Rc::clone(&self.function);
1647 let next_fn = Rc::clone(&next.function);
1648 RcTester {
1649 function: Rc::new(move || self_fn() ^ next_fn()),
1650 }
1651 }
1652
1653 /// Combines this tester with another tester using logical NOR
1654 ///
1655 /// Returns a new `RcTester` that returns `true` only when both tests
1656 /// fail. Borrows `&self`, so the original tester remains available.
1657 ///
1658 /// # Parameters
1659 ///
1660 /// * `next` - The tester to combine with
1661 ///
1662 /// # Return Value
1663 ///
1664 /// A new `RcTester` representing logical NOR
1665 ///
1666 /// # Examples
1667 ///
1668 /// ```rust
1669 /// use prism3_function::{RcTester, Tester};
1670 ///
1671 /// let first = RcTester::new(|| false);
1672 /// let second = RcTester::new(|| false);
1673 /// let nor = first.nor(&second);
1674 ///
1675 /// // Both false returns true
1676 /// assert!(nor.test());
1677 ///
1678 /// // first and second still available
1679 /// assert!(!first.test());
1680 /// assert!(!second.test());
1681 /// ```
1682 pub fn nor(&self, next: &RcTester) -> RcTester {
1683 let self_fn = Rc::clone(&self.function);
1684 let next_fn = Rc::clone(&next.function);
1685 RcTester {
1686 function: Rc::new(move || !(self_fn() || next_fn())),
1687 }
1688 }
1689}
1690
1691impl Tester for RcTester {
1692 fn test(&self) -> bool {
1693 (self.function)()
1694 }
1695
1696 fn into_box(self) -> BoxTester {
1697 BoxTester {
1698 function: Box::new(move || (self.function)()),
1699 }
1700 }
1701
1702 fn into_rc(self) -> RcTester {
1703 self
1704 }
1705
1706 // Note: RcTester is not Send + Sync, so into_arc() cannot be
1707 // implemented. Calling into_arc() on RcTester will result in a
1708 // compile error due to the Send + Sync trait bounds not being
1709 // satisfied. The default Tester trait implementation will be used.
1710
1711 fn into_fn(self) -> impl Fn() -> bool {
1712 move || (self.function)()
1713 }
1714
1715 fn to_box(&self) -> BoxTester {
1716 let self_fn = self.function.clone();
1717 BoxTester {
1718 function: Box::new(move || self_fn()),
1719 }
1720 }
1721
1722 fn to_rc(&self) -> RcTester {
1723 self.clone()
1724 }
1725
1726 // Note: RcTester is not Send + Sync, so to_arc() cannot be
1727 // implemented. Calling to_arc() on RcTester will result in a compile
1728 // error due to the Send + Sync trait bounds not being satisfied. The
1729 // default Tester trait implementation will be used.
1730
1731 fn to_fn(&self) -> impl Fn() -> bool {
1732 let self_fn = self.function.clone();
1733 move || self_fn()
1734 }
1735}
1736
1737impl Clone for RcTester {
1738 /// Creates a clone of this `RcTester`.
1739 ///
1740 /// The cloned instance shares the same underlying function with
1741 /// the original, allowing multiple references to the same test
1742 /// logic.
1743 fn clone(&self) -> Self {
1744 Self {
1745 function: Rc::clone(&self.function),
1746 }
1747 }
1748}
1749
1750// ============================================================================
1751// Tester Implementation for Closures
1752// ============================================================================
1753
1754impl<F> Tester for F
1755where
1756 F: Fn() -> bool,
1757{
1758 fn test(&self) -> bool {
1759 self()
1760 }
1761
1762 fn into_box(self) -> BoxTester
1763 where
1764 Self: Sized + 'static,
1765 {
1766 BoxTester::new(self)
1767 }
1768
1769 fn into_rc(self) -> RcTester
1770 where
1771 Self: Sized + 'static,
1772 {
1773 RcTester::new(self)
1774 }
1775
1776 fn into_arc(self) -> ArcTester
1777 where
1778 Self: Sized + Send + Sync + 'static,
1779 {
1780 ArcTester::new(self)
1781 }
1782
1783 fn into_fn(self) -> impl Fn() -> bool
1784 where
1785 Self: Sized + 'static,
1786 {
1787 self
1788 }
1789
1790 fn to_box(&self) -> BoxTester
1791 where
1792 Self: Clone + Sized + 'static,
1793 {
1794 self.clone().into_box()
1795 }
1796
1797 fn to_rc(&self) -> RcTester
1798 where
1799 Self: Clone + Sized + 'static,
1800 {
1801 self.clone().into_rc()
1802 }
1803
1804 fn to_arc(&self) -> ArcTester
1805 where
1806 Self: Clone + Sized + Send + Sync + 'static,
1807 {
1808 self.clone().into_arc()
1809 }
1810
1811 fn to_fn(&self) -> impl Fn() -> bool
1812 where
1813 Self: Clone + Sized,
1814 {
1815 self.clone()
1816 }
1817}
1818
1819// ============================================================================
1820// Extension Trait for Convenient Closure Conversion
1821// ============================================================================
1822
1823/// Extension trait providing logical composition methods for closures
1824///
1825/// This trait is automatically implemented for all closures and function
1826/// pointers that match `Fn() -> bool`, enabling method chaining starting
1827/// from a closure.
1828///
1829/// # Examples
1830///
1831/// ```rust
1832/// use prism3_function::{FnTesterOps, Tester};
1833///
1834/// let is_ready = || true;
1835/// let is_available = || true;
1836///
1837/// // Combine testers using extension methods
1838/// let combined = is_ready.and(is_available);
1839/// assert!(combined.test());
1840/// ```
1841///
1842/// # Author
1843///
1844/// Hu Haixing
1845pub trait FnTesterOps: Sized + Fn() -> bool + 'static {
1846 /// Returns a tester that represents the logical AND of this tester
1847 /// and another
1848 ///
1849 /// # Parameters
1850 ///
1851 /// * `other` - The other tester to combine with. **Note: This parameter
1852 /// is passed by value and will transfer ownership.** If you need to
1853 /// preserve the original tester, clone it first (if it implements
1854 /// `Clone`). Can be:
1855 /// - Another closure
1856 /// - A function pointer
1857 /// - A `BoxTester`, `RcTester`, or `ArcTester`
1858 ///
1859 /// # Return Value
1860 ///
1861 /// A `BoxTester` representing the logical AND
1862 ///
1863 /// # Examples
1864 ///
1865 /// ```rust
1866 /// use prism3_function::{FnTesterOps, Tester};
1867 ///
1868 /// let is_ready = || true;
1869 /// let is_available = || true;
1870 ///
1871 /// let combined = is_ready.and(is_available);
1872 /// assert!(combined.test());
1873 /// ```
1874 fn and<T>(self, other: T) -> BoxTester
1875 where
1876 T: Tester + 'static,
1877 {
1878 BoxTester::new(move || self.test() && other.test())
1879 }
1880
1881 /// Returns a tester that represents the logical OR of this tester
1882 /// and another
1883 ///
1884 /// # Parameters
1885 ///
1886 /// * `other` - The other tester to combine with. **Note: This parameter
1887 /// is passed by value and will transfer ownership.** If you need to
1888 /// preserve the original tester, clone it first (if it implements
1889 /// `Clone`). Can be:
1890 /// - Another closure
1891 /// - A function pointer
1892 /// - A `BoxTester`, `RcTester`, or `ArcTester`
1893 /// - Any type implementing `Tester`
1894 ///
1895 /// # Return Value
1896 ///
1897 /// A `BoxTester` representing the logical OR
1898 ///
1899 /// # Examples
1900 ///
1901 /// ```rust
1902 /// use prism3_function::{FnTesterOps, Tester};
1903 ///
1904 /// let is_ready = || false;
1905 /// let is_fallback = || true;
1906 ///
1907 /// let combined = is_ready.or(is_fallback);
1908 /// assert!(combined.test());
1909 /// ```
1910 fn or<T>(self, other: T) -> BoxTester
1911 where
1912 T: Tester + 'static,
1913 {
1914 BoxTester::new(move || self.test() || other.test())
1915 }
1916
1917 /// Returns a tester that represents the logical negation of this tester
1918 ///
1919 /// # Return Value
1920 ///
1921 /// A `BoxTester` representing the logical negation
1922 ///
1923 /// # Examples
1924 ///
1925 /// ```rust
1926 /// use prism3_function::{FnTesterOps, Tester};
1927 ///
1928 /// let is_ready = || false;
1929 /// let not_ready = is_ready.not();
1930 /// assert!(not_ready.test());
1931 /// ```
1932 fn not(self) -> BoxTester {
1933 BoxTester::new(move || !self.test())
1934 }
1935
1936 /// Returns a tester that represents the logical NAND (NOT AND) of this
1937 /// tester and another
1938 ///
1939 /// NAND returns `true` unless both testers are `true`.
1940 /// Equivalent to `!(self AND other)`.
1941 ///
1942 /// # Parameters
1943 ///
1944 /// * `other` - The other tester to combine with. **Note: This parameter
1945 /// is passed by value and will transfer ownership.** If you need to
1946 /// preserve the original tester, clone it first (if it implements
1947 /// `Clone`). Accepts closures, function pointers, or any
1948 /// `Tester` implementation.
1949 ///
1950 /// # Return Value
1951 ///
1952 /// A `BoxTester` representing the logical NAND
1953 ///
1954 /// # Examples
1955 ///
1956 /// ```rust
1957 /// use prism3_function::{FnTesterOps, Tester};
1958 ///
1959 /// let is_ready = || true;
1960 /// let is_available = || true;
1961 ///
1962 /// let nand = is_ready.nand(is_available);
1963 /// assert!(!nand.test()); // !(true && true) = false
1964 /// ```
1965 fn nand<T>(self, other: T) -> BoxTester
1966 where
1967 T: Tester + 'static,
1968 {
1969 BoxTester::new(move || !(self.test() && other.test()))
1970 }
1971
1972 /// Returns a tester that represents the logical XOR (exclusive OR) of
1973 /// this tester and another
1974 ///
1975 /// XOR returns `true` if exactly one of the testers is `true`.
1976 ///
1977 /// # Parameters
1978 ///
1979 /// * `other` - The other tester to combine with. **Note: This parameter
1980 /// is passed by value and will transfer ownership.** If you need to
1981 /// preserve the original tester, clone it first (if it implements
1982 /// `Clone`). Accepts closures, function pointers, or any
1983 /// `Tester` implementation.
1984 ///
1985 /// # Return Value
1986 ///
1987 /// A `BoxTester` representing the logical XOR
1988 ///
1989 /// # Examples
1990 ///
1991 /// ```rust
1992 /// use prism3_function::{FnTesterOps, Tester};
1993 ///
1994 /// let is_ready = || true;
1995 /// let is_available = || false;
1996 ///
1997 /// let xor = is_ready.xor(is_available);
1998 /// assert!(xor.test()); // true ^ false = true
1999 /// ```
2000 fn xor<T>(self, other: T) -> BoxTester
2001 where
2002 T: Tester + 'static,
2003 {
2004 BoxTester::new(move || self.test() ^ other.test())
2005 }
2006
2007 /// Returns a tester that represents the logical NOR (NOT OR) of this
2008 /// tester and another
2009 ///
2010 /// NOR returns `true` only when both testers are `false`. Equivalent
2011 /// to `!(self OR other)`.
2012 ///
2013 /// # Parameters
2014 ///
2015 /// * `other` - The other tester to combine with. **Note: This parameter
2016 /// is passed by value and will transfer ownership.** If you need to
2017 /// preserve the original tester, clone it first (if it implements
2018 /// `Clone`). Accepts closures, function pointers, or any
2019 /// `Tester` implementation.
2020 ///
2021 /// # Return Value
2022 ///
2023 /// A `BoxTester` representing the logical NOR
2024 ///
2025 /// # Examples
2026 ///
2027 /// ```rust
2028 /// use prism3_function::{FnTesterOps, Tester};
2029 ///
2030 /// let is_ready = || false;
2031 /// let is_available = || false;
2032 ///
2033 /// let nor = is_ready.nor(is_available);
2034 /// assert!(nor.test()); // !(false || false) = true
2035 /// ```
2036 fn nor<T>(self, other: T) -> BoxTester
2037 where
2038 T: Tester + 'static,
2039 {
2040 BoxTester::new(move || !(self.test() || other.test()))
2041 }
2042}
2043
2044// Blanket implementation for all closures
2045impl<F> FnTesterOps for F where F: Fn() -> bool + 'static {}