proptest/strategy/
filter_map.rs1use crate::std_facade::{fmt, Arc, Cell};
11
12use crate::strategy::traits::*;
13use crate::test_runner::*;
14
15#[must_use = "strategies do nothing unless used"]
19pub struct FilterMap<S, F> {
20 pub(super) source: S,
21 pub(super) whence: Reason,
22 pub(super) fun: Arc<F>,
23}
24
25impl<S, F> FilterMap<S, F> {
26 pub(super) fn new(source: S, whence: Reason, fun: F) -> Self {
27 Self {
28 source,
29 whence,
30 fun: Arc::new(fun),
31 }
32 }
33}
34
35impl<S: fmt::Debug, F> fmt::Debug for FilterMap<S, F> {
36 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
37 f.debug_struct("FilterMap")
38 .field("source", &self.source)
39 .field("whence", &self.whence)
40 .field("fun", &"<function>")
41 .finish()
42 }
43}
44
45impl<S: Clone, F> Clone for FilterMap<S, F> {
46 fn clone(&self) -> Self {
47 Self {
48 source: self.source.clone(),
49 whence: self.whence.clone(),
50 fun: Arc::clone(&self.fun),
51 }
52 }
53}
54
55impl<S: Strategy, F: Fn(S::Value) -> Option<O>, O: fmt::Debug> Strategy
56 for FilterMap<S, F>
57{
58 type Tree = FilterMapValueTree<S::Tree, F, O>;
59 type Value = O;
60
61 fn new_tree(&self, runner: &mut TestRunner) -> NewTree<Self> {
62 loop {
63 let val = self.source.new_tree(runner)?;
64 if let Some(current) = (self.fun)(val.current()) {
65 return Ok(FilterMapValueTree::new(val, &self.fun, current));
66 } else {
67 runner.reject_local(self.whence.clone())?;
68 }
69 }
70 }
71}
72
73pub struct FilterMapValueTree<V, F, O> {
75 source: V,
76 current: Cell<Option<O>>,
77 fun: Arc<F>,
78}
79
80impl<V: Clone + ValueTree, F: Fn(V::Value) -> Option<O>, O> Clone
81 for FilterMapValueTree<V, F, O>
82{
83 fn clone(&self) -> Self {
84 Self::new(self.source.clone(), &self.fun, self.fresh_current())
85 }
86}
87
88impl<V: fmt::Debug, F, O> fmt::Debug for FilterMapValueTree<V, F, O> {
89 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
90 f.debug_struct("FilterMapValueTree")
91 .field("source", &self.source)
92 .field("current", &"<current>")
93 .field("fun", &"<function>")
94 .finish()
95 }
96}
97
98impl<V: ValueTree, F: Fn(V::Value) -> Option<O>, O>
99 FilterMapValueTree<V, F, O>
100{
101 fn new(source: V, fun: &Arc<F>, current: O) -> Self {
102 Self {
103 source,
104 current: Cell::new(Some(current)),
105 fun: Arc::clone(fun),
106 }
107 }
108
109 fn fresh_current(&self) -> O {
110 (self.fun)(self.source.current())
111 .expect("internal logic error; this is a bug!")
112 }
113
114 fn ensure_acceptable(&mut self) {
115 loop {
116 if let Some(current) = (self.fun)(self.source.current()) {
117 self.current = Cell::new(Some(current));
119 break;
120 } else if !self.source.complicate() {
121 panic!(
122 "Unable to complicate filtered strategy \
123 back into acceptable value"
124 );
125 }
126 }
127 }
128}
129
130impl<V: ValueTree, F: Fn(V::Value) -> Option<O>, O: fmt::Debug> ValueTree
131 for FilterMapValueTree<V, F, O>
132{
133 type Value = O;
134
135 fn current(&self) -> O {
136 if let Some(current) = self.current.replace(None) {
139 current
140 } else {
141 self.fresh_current()
142 }
143 }
144
145 fn simplify(&mut self) -> bool {
146 if self.source.simplify() {
147 self.ensure_acceptable();
148 true
149 } else {
150 false
151 }
152 }
153
154 fn complicate(&mut self) -> bool {
155 if self.source.complicate() {
156 self.ensure_acceptable();
157 true
158 } else {
159 false
160 }
161 }
162}
163
164#[cfg(test)]
165mod test {
166 use super::*;
167
168 #[test]
169 fn test_filter_map() {
170 let input = (0..256).prop_filter_map("%3 + 1", |v| {
171 if 0 == v % 3 {
172 Some(v + 1)
173 } else {
174 None
175 }
176 });
177
178 for _ in 0..256 {
179 let mut runner = TestRunner::default();
180 let mut case = input.new_tree(&mut runner).unwrap();
181
182 assert_eq!(0, (case.current() - 1) % 3);
183
184 while case.simplify() {
185 assert_eq!(0, (case.current() - 1) % 3);
186 }
187 assert_eq!(0, (case.current() - 1) % 3);
188 }
189 }
190
191 #[test]
192 fn test_filter_map_sanity() {
193 check_strategy_sanity(
194 (0..256).prop_filter_map("!%5 * 2", |v| {
195 if 0 != v % 5 {
196 Some(v * 2)
197 } else {
198 None
199 }
200 }),
201 Some(CheckStrategySanityOptions {
202 strict_complicate_after_simplify: false,
205 ..CheckStrategySanityOptions::default()
206 }),
207 );
208 }
209}