1use crate::{Stop, StopReason};
42
43pub struct FnStop<F> {
62 f: F,
63}
64
65impl<F> FnStop<F>
66where
67 F: Fn() -> bool + Send + Sync,
68{
69 #[inline]
73 pub fn new(f: F) -> Self {
74 Self { f }
75 }
76}
77
78impl<F> Stop for FnStop<F>
79where
80 F: Fn() -> bool + Send + Sync,
81{
82 #[inline]
83 #[track_caller]
84 fn check(&self) -> Result<(), StopReason> {
85 if (self.f)() {
86 Err(StopReason::Cancelled)
87 } else {
88 Ok(())
89 }
90 }
91
92 #[inline]
93 #[track_caller]
94 fn should_stop(&self) -> bool {
95 (self.f)()
96 }
97}
98
99impl<F: Clone> Clone for FnStop<F> {
100 fn clone(&self) -> Self {
101 Self { f: self.f.clone() }
102 }
103}
104
105impl<F: Copy> Copy for FnStop<F> {}
106
107impl<F> core::fmt::Debug for FnStop<F> {
108 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
109 f.debug_struct("FnStop").finish_non_exhaustive()
110 }
111}
112
113#[cfg(test)]
114mod tests {
115 use super::*;
116 use core::sync::atomic::{AtomicBool, Ordering};
117
118 #[test]
119 fn fn_stop_basic() {
120 let flag = AtomicBool::new(false);
121 let stop = FnStop::new(|| flag.load(Ordering::Relaxed));
122
123 assert!(!stop.should_stop());
124 assert!(stop.check().is_ok());
125
126 flag.store(true, Ordering::Relaxed);
127
128 assert!(stop.should_stop());
129 assert_eq!(stop.check(), Err(StopReason::Cancelled));
130 }
131
132 #[test]
133 fn fn_stop_with_static() {
134 static FLAG: AtomicBool = AtomicBool::new(false);
135
136 let stop = FnStop::new(|| FLAG.load(Ordering::Relaxed));
137 assert!(!stop.should_stop());
138
139 FLAG.store(true, Ordering::Relaxed);
140 assert!(stop.should_stop());
141
142 FLAG.store(false, Ordering::Relaxed);
144 }
145
146 #[test]
147 fn fn_stop_always_true() {
148 let stop = FnStop::new(|| true);
149 assert!(stop.should_stop());
150 assert_eq!(stop.check(), Err(StopReason::Cancelled));
151 }
152
153 #[test]
154 fn fn_stop_always_false() {
155 let stop = FnStop::new(|| false);
156 assert!(!stop.should_stop());
157 assert!(stop.check().is_ok());
158 }
159
160 #[test]
161 fn fn_stop_is_send_sync() {
162 fn assert_send_sync<T: Send + Sync>() {}
163 assert_send_sync::<FnStop<fn() -> bool>>();
164 }
165
166 #[test]
167 fn fn_stop_copy() {
168 let stop: FnStop<fn() -> bool> = FnStop::new(|| false);
170 let stop2 = stop; assert!(!stop.should_stop()); assert!(!stop2.should_stop());
173 }
174}
175
176#[cfg(all(test, feature = "alloc"))]
177mod alloc_tests {
178 use super::*;
179
180 #[test]
181 fn fn_stop_debug() {
182 extern crate alloc;
183 let stop = FnStop::new(|| false);
184 let debug = alloc::format!("{:?}", stop);
185 assert!(debug.contains("FnStop"));
186 }
187}