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