qubit_function/tester/box_tester.rs
1/*******************************************************************************
2 *
3 * Copyright (c) 2025 - 2026 Haixing Hu.
4 *
5 * SPDX-License-Identifier: Apache-2.0
6 *
7 * Licensed under the Apache License, Version 2.0.
8 *
9 ******************************************************************************/
10// qubit-style: allow explicit-imports
11//! Defines the `BoxTester` public type.
12
13#![allow(unused_imports)]
14
15use super::*;
16
17// ============================================================================
18// BoxTester: Single Ownership Implementation
19// ============================================================================
20
21/// Single ownership Tester implemented using `Box`
22///
23/// `BoxTester` wraps a closure in `Box<dyn Fn() -> bool>`, providing single
24/// ownership semantics with no additional allocation overhead beyond the
25/// initial boxing.
26///
27/// # Characteristics
28///
29/// - **Single ownership**: Cannot be cloned
30/// - **Zero overhead**: Single heap allocation
31/// - **Consuming combination**: `and()`/`or()`/`not()` consume `self`
32/// - **Type flexibility**: Accepts any `Tester` implementation
33///
34/// # Use Cases
35///
36/// - One-time testing scenarios
37/// - Builder patterns requiring ownership transfer
38/// - Simple state checking without sharing
39/// - Chained calls with ownership transfer
40///
41/// # Examples
42///
43/// ```rust
44/// use qubit_function::{BoxTester, Tester};
45/// use std::sync::{Arc, atomic::{AtomicUsize, Ordering}};
46///
47/// // State managed externally
48/// let count = Arc::new(AtomicUsize::new(0));
49/// let count_clone = Arc::clone(&count);
50///
51/// let tester = BoxTester::new(move || {
52/// count_clone.load(Ordering::Relaxed) < 3
53/// });
54///
55/// assert!(tester.test());
56/// count.fetch_add(1, Ordering::Relaxed);
57/// assert!(tester.test());
58/// count.fetch_add(1, Ordering::Relaxed);
59/// assert!(tester.test());
60/// count.fetch_add(1, Ordering::Relaxed);
61/// assert!(!tester.test());
62///
63/// // Logical combination
64/// let combined = BoxTester::new(|| true)
65/// .and(|| false)
66/// .or(|| true);
67/// assert!(combined.test());
68/// ```
69///
70pub struct BoxTester {
71 pub(super) function: Box<dyn Fn() -> bool>,
72}
73
74impl BoxTester {
75 /// Creates a new `BoxTester` from a closure
76 ///
77 /// # Type Parameters
78 ///
79 /// * `F` - Closure type implementing `Fn() -> bool`
80 ///
81 /// # Parameters
82 ///
83 /// * `f` - The closure to wrap
84 ///
85 /// # Return Value
86 ///
87 /// A new `BoxTester` instance
88 ///
89 /// # Examples
90 ///
91 /// ```rust
92 /// use qubit_function::BoxTester;
93 ///
94 /// let tester = BoxTester::new(|| true);
95 /// ```
96 #[inline]
97 pub fn new<F>(f: F) -> Self
98 where
99 F: Fn() -> bool + 'static,
100 {
101 BoxTester {
102 function: Box::new(f),
103 }
104 }
105
106 /// Combines this tester with another tester using logical AND
107 ///
108 /// Returns a new `BoxTester` that returns `true` only when both tests
109 /// pass. Short-circuit evaluation: if the first test fails, the second
110 /// will not be executed.
111 ///
112 /// # Type Parameters
113 ///
114 /// * `T` - Type implementing `Tester`
115 ///
116 /// # Parameters
117 ///
118 /// * `next` - The tester to combine with
119 ///
120 /// # Return Value
121 ///
122 /// A new `BoxTester` representing logical AND
123 ///
124 /// # Examples
125 ///
126 /// ```rust
127 /// use qubit_function::{BoxTester, Tester};
128 /// use std::sync::{Arc, atomic::{AtomicUsize, AtomicBool, Ordering}};
129 ///
130 /// // Simulate service status
131 /// let request_count = Arc::new(AtomicUsize::new(0));
132 /// let is_available = Arc::new(AtomicBool::new(true));
133 /// let max_requests = 1000;
134 ///
135 /// let count_clone = Arc::clone(&request_count);
136 /// let available_clone = Arc::clone(&is_available);
137 ///
138 /// // Service available and request count not exceeded
139 /// let service_ok = BoxTester::new(move || {
140 /// available_clone.load(Ordering::Relaxed)
141 /// })
142 /// .and(move || {
143 /// count_clone.load(Ordering::Relaxed) < max_requests
144 /// });
145 ///
146 /// // Initial state: available and request count 0
147 /// assert!(service_ok.test());
148 ///
149 /// // Simulate request increase
150 /// request_count.store(500, Ordering::Relaxed);
151 /// assert!(service_ok.test());
152 ///
153 /// // Request count exceeded
154 /// request_count.store(1500, Ordering::Relaxed);
155 /// assert!(!service_ok.test());
156 ///
157 /// // Service unavailable
158 /// is_available.store(false, Ordering::Relaxed);
159 /// assert!(!service_ok.test());
160 /// ```
161 #[inline]
162 pub fn and<T>(self, next: T) -> BoxTester
163 where
164 T: Tester + 'static,
165 {
166 let self_fn = self.function;
167 let next_tester = next;
168 BoxTester::new(move || self_fn() && next_tester.test())
169 }
170
171 /// Combines this tester with another tester using logical OR
172 ///
173 /// Returns a new `BoxTester` that returns `true` if either test passes.
174 /// Short-circuit evaluation: if the first test passes, the second will
175 /// not be executed.
176 ///
177 /// # Type Parameters
178 ///
179 /// * `T` - Type implementing `Tester`
180 ///
181 /// # Parameters
182 ///
183 /// * `next` - The tester to combine with
184 ///
185 /// # Return Value
186 ///
187 /// A new `BoxTester` representing logical OR
188 ///
189 /// # Examples
190 ///
191 /// ```rust
192 /// use qubit_function::{BoxTester, Tester};
193 /// use std::sync::{Arc, atomic::{AtomicUsize, AtomicBool, Ordering}};
194 ///
195 /// // Simulate service status
196 /// let request_count = Arc::new(AtomicUsize::new(0));
197 /// let is_healthy = Arc::new(AtomicBool::new(true));
198 /// let max_requests = 100;
199 ///
200 /// let count_clone = Arc::clone(&request_count);
201 /// let health_clone = Arc::clone(&is_healthy);
202 ///
203 /// // Service healthy or low request count
204 /// let can_serve = BoxTester::new(move || {
205 /// health_clone.load(Ordering::Relaxed)
206 /// })
207 /// .or(move || {
208 /// count_clone.load(Ordering::Relaxed) < max_requests
209 /// });
210 ///
211 /// // Initial state: healthy and request count 0
212 /// assert!(can_serve.test());
213 ///
214 /// // Request count increased but within limit
215 /// request_count.store(50, Ordering::Relaxed);
216 /// assert!(can_serve.test());
217 ///
218 /// // Request count exceeded but service healthy
219 /// request_count.store(150, Ordering::Relaxed);
220 /// assert!(can_serve.test()); // still healthy
221 ///
222 /// // Service unhealthy but low request count
223 /// is_healthy.store(false, Ordering::Relaxed);
224 /// request_count.store(50, Ordering::Relaxed);
225 /// assert!(can_serve.test()); // low request count
226 ///
227 /// // Unhealthy and high request count
228 /// request_count.store(150, Ordering::Relaxed);
229 /// assert!(!can_serve.test());
230 /// ```
231 #[inline]
232 pub fn or<T>(self, next: T) -> BoxTester
233 where
234 T: Tester + 'static,
235 {
236 let self_fn = self.function;
237 let next_tester = next;
238 BoxTester::new(move || self_fn() || next_tester.test())
239 }
240
241 /// Negates the result of this tester
242 ///
243 /// Returns a new `BoxTester` that returns the opposite value of the
244 /// original test result.
245 ///
246 /// # Return Value
247 ///
248 /// A new `BoxTester` representing logical NOT
249 ///
250 /// # Examples
251 ///
252 /// ```rust
253 /// use qubit_function::{BoxTester, Tester};
254 /// use std::sync::{Arc, atomic::{AtomicUsize, Ordering}};
255 ///
256 /// // Simulate resource usage
257 /// let memory_usage = Arc::new(AtomicUsize::new(0));
258 /// let max_memory = 1024; // MB
259 ///
260 /// let memory_clone = Arc::clone(&memory_usage);
261 ///
262 /// // Memory usage not exceeded
263 /// let memory_ok = BoxTester::new(move || {
264 /// memory_clone.load(Ordering::Relaxed) <= max_memory
265 /// });
266 ///
267 /// // Initial state: normal memory usage
268 /// memory_usage.store(512, Ordering::Relaxed);
269 /// assert!(memory_ok.test());
270 ///
271 /// // Memory usage exceeded (negated)
272 /// let memory_critical = memory_ok.not();
273 /// assert!(!memory_critical.test());
274 ///
275 /// // Memory usage exceeded
276 /// memory_usage.store(2048, Ordering::Relaxed);
277 /// assert!(memory_critical.test());
278 /// ```
279 #[allow(clippy::should_implement_trait)]
280 #[inline]
281 pub fn not(self) -> BoxTester {
282 let self_fn = self.function;
283 BoxTester::new(move || !self_fn())
284 }
285
286 /// Combines this tester with another tester using logical NAND
287 ///
288 /// Returns a new `BoxTester` that returns `true` unless both tests pass.
289 /// Equivalent to `!(self AND other)`.
290 ///
291 /// # Type Parameters
292 ///
293 /// * `T` - Type implementing `Tester`
294 ///
295 /// # Parameters
296 ///
297 /// * `next` - The tester to combine with
298 ///
299 /// # Return Value
300 ///
301 /// A new `BoxTester` representing logical NAND
302 ///
303 /// # Examples
304 ///
305 /// ```rust
306 /// use qubit_function::{BoxTester, Tester};
307 /// use std::sync::{Arc, atomic::{AtomicBool, Ordering}};
308 ///
309 /// let flag1 = Arc::new(AtomicBool::new(true));
310 /// let flag2 = Arc::new(AtomicBool::new(true));
311 ///
312 /// let flag1_clone = Arc::clone(&flag1);
313 /// let flag2_clone = Arc::clone(&flag2);
314 ///
315 /// let nand = BoxTester::new(move || {
316 /// flag1_clone.load(Ordering::Relaxed)
317 /// })
318 /// .nand(move || {
319 /// flag2_clone.load(Ordering::Relaxed)
320 /// });
321 ///
322 /// // Both true returns false
323 /// assert!(!nand.test());
324 ///
325 /// // At least one false returns true
326 /// flag1.store(false, Ordering::Relaxed);
327 /// assert!(nand.test());
328 /// ```
329 #[inline]
330 pub fn nand<T>(self, next: T) -> BoxTester
331 where
332 T: Tester + 'static,
333 {
334 let self_fn = self.function;
335 let next_tester = next;
336 BoxTester::new(move || !(self_fn() && next_tester.test()))
337 }
338
339 /// Combines this tester with another tester using logical XOR
340 ///
341 /// Returns a new `BoxTester` that returns `true` if exactly one test
342 /// passes.
343 ///
344 /// # Type Parameters
345 ///
346 /// * `T` - Type implementing `Tester`
347 ///
348 /// # Parameters
349 ///
350 /// * `next` - The tester to combine with
351 ///
352 /// # Return Value
353 ///
354 /// A new `BoxTester` representing logical XOR
355 ///
356 /// # Examples
357 ///
358 /// ```rust
359 /// use qubit_function::{BoxTester, Tester};
360 /// use std::sync::{Arc, atomic::{AtomicBool, Ordering}};
361 ///
362 /// let flag1 = Arc::new(AtomicBool::new(true));
363 /// let flag2 = Arc::new(AtomicBool::new(false));
364 ///
365 /// let flag1_clone1 = Arc::clone(&flag1);
366 /// let flag2_clone1 = Arc::clone(&flag2);
367 ///
368 /// let xor = BoxTester::new(move || {
369 /// flag1_clone1.load(Ordering::Relaxed)
370 /// })
371 /// .xor(move || {
372 /// flag2_clone1.load(Ordering::Relaxed)
373 /// });
374 ///
375 /// // One true one false returns true
376 /// assert!(xor.test());
377 ///
378 /// // Both true returns false
379 /// flag2.store(true, Ordering::Relaxed);
380 /// assert!(!xor.test());
381 ///
382 /// // Both false returns false
383 /// flag1.store(false, Ordering::Relaxed);
384 /// flag2.store(false, Ordering::Relaxed);
385 /// assert!(!xor.test());
386 /// ```
387 #[inline]
388 pub fn xor<T>(self, next: T) -> BoxTester
389 where
390 T: Tester + 'static,
391 {
392 let self_fn = self.function;
393 let next_tester = next;
394 BoxTester::new(move || self_fn() ^ next_tester.test())
395 }
396
397 /// Combines this tester with another tester using logical NOR
398 ///
399 /// Returns a new `BoxTester` that returns `true` only when both tests
400 /// fail. Equivalent to `!(self OR other)`.
401 ///
402 /// # Type Parameters
403 ///
404 /// * `T` - Type implementing `Tester`
405 ///
406 /// # Parameters
407 ///
408 /// * `next` - The tester to combine with
409 ///
410 /// # Return Value
411 ///
412 /// A new `BoxTester` representing logical NOR
413 ///
414 /// # Examples
415 ///
416 /// ```rust
417 /// use qubit_function::{BoxTester, Tester};
418 /// use std::sync::{Arc, atomic::{AtomicBool, Ordering}};
419 ///
420 /// let flag1 = Arc::new(AtomicBool::new(false));
421 /// let flag2 = Arc::new(AtomicBool::new(false));
422 ///
423 /// let flag1_clone = Arc::clone(&flag1);
424 /// let flag2_clone = Arc::clone(&flag2);
425 ///
426 /// let nor = BoxTester::new(move || {
427 /// flag1_clone.load(Ordering::Relaxed)
428 /// })
429 /// .nor(move || {
430 /// flag2_clone.load(Ordering::Relaxed)
431 /// });
432 ///
433 /// // Both false returns true
434 /// assert!(nor.test());
435 ///
436 /// // At least one true returns false
437 /// flag1.store(true, Ordering::Relaxed);
438 /// assert!(!nor.test());
439 /// ```
440 #[inline]
441 pub fn nor<T>(self, next: T) -> BoxTester
442 where
443 T: Tester + 'static,
444 {
445 let self_fn = self.function;
446 let next_tester = next;
447 BoxTester::new(move || !(self_fn() || next_tester.test()))
448 }
449}
450
451impl Tester for BoxTester {
452 #[inline]
453 fn test(&self) -> bool {
454 (self.function)()
455 }
456
457 #[inline]
458 fn into_box(self) -> BoxTester {
459 self
460 }
461
462 #[inline]
463 fn into_rc(self) -> RcTester {
464 let func = self.function;
465 RcTester {
466 function: Rc::new(func),
467 }
468 }
469
470 // Note: BoxTester does not implement Send + Sync, so into_arc()
471 // cannot be implemented. Calling into_arc() on BoxTester will result
472 // in a compile error due to the Send + Sync trait bounds not being
473 // satisfied. The default Tester trait implementation will be used.
474
475 #[inline]
476 fn into_fn(self) -> impl Fn() -> bool {
477 self.function
478 }
479
480 // Note: BoxTester does not implement Clone, so to_box(), to_rc(),
481 // to_arc(), and to_fn() cannot be implemented. Calling these methods
482 // on BoxTester will result in a compile error due to the Clone trait
483 // bound not being satisfied. The default Tester trait implementations
484 // will be used.
485}