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