1use pretty::{BoxAllocator, DocAllocator};
6use std::fmt::{self, Debug, Display, Formatter};
7use std::sync::Arc;
8
9pub mod asynchronous;
11pub mod host_driver;
13pub mod peer;
15#[macro_use]
17pub mod prelude;
18#[cfg(test)]
20pub mod test;
21
22#[derive(Clone, PartialEq)]
29pub struct DebugString(String);
30
31const FMT_CHAR_WIDTH: usize = 100;
33
34impl fmt::Debug for DebugString {
35 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
36 write!(f, "{}", self.0)
37 }
38}
39
40type DocBuilder<'a> = pretty::DocBuilder<'a, BoxAllocator>;
42
43fn parens<'a>(alloc: &'a BoxAllocator, doc: DocBuilder<'a>) -> DocBuilder<'a> {
44 alloc.text("(").append(doc).append(alloc.text(")"))
45}
46
47fn show_debug<T: Debug>(t: &T) -> String {
49 format!("{:?}", t)
50}
51
52type Comparison<T> = Arc<dyn Fn(&T, &T) -> bool + Send + Sync + 'static>;
54type Show<T> = Arc<dyn Fn(&T) -> String + Send + Sync + 'static>;
56
57pub enum Predicate<T> {
61 Equal(Arc<T>, Comparison<T>, Show<T>),
62 And(Box<Predicate<T>>, Box<Predicate<T>>),
63 Or(Box<Predicate<T>>, Box<Predicate<T>>),
64 Not(Box<Predicate<T>>),
65 Predicate(Arc<dyn Fn(&T) -> bool + Send + Sync>, String),
66 Over(Arc<dyn IsOver<T> + Send + Sync>),
70 Any(Arc<dyn IsAny<T> + Send + Sync + 'static>),
75 All(Arc<dyn IsAll<T> + Send + Sync + 'static>),
80}
81
82impl<T> Clone for Predicate<T> {
87 fn clone(&self) -> Self {
88 match self {
89 Predicate::Equal(t, comp, repr) => {
90 Predicate::Equal(t.clone(), comp.clone(), repr.clone())
91 }
92 Predicate::And(l, r) => Predicate::And(l.clone(), r.clone()),
93 Predicate::Or(l, r) => Predicate::Or(l.clone(), r.clone()),
94 Predicate::Not(x) => Predicate::Not(x.clone()),
95 Predicate::Predicate(p, msg) => Predicate::Predicate(p.clone(), msg.clone()),
96 Predicate::Over(x) => Predicate::Over(x.clone()),
97 Predicate::Any(x) => Predicate::Any(x.clone()),
98 Predicate::All(x) => Predicate::All(x.clone()),
99 }
100 }
101}
102
103impl<T> Debug for Predicate<T> {
104 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
105 self.describe(&BoxAllocator).1.pretty(FMT_CHAR_WIDTH).fmt(f)
106 }
107}
108
109const MAX_ITER_FALSIFICATIONS: usize = 5;
112
113fn falsify_elem<'p, 't, 'd, T: Debug>(
114 pred: &'p Predicate<T>,
115 index: usize,
116 el: &'t T,
117 doc: &'d BoxAllocator,
118) -> Option<DocBuilder<'d>> {
119 pred.falsify(el, doc).map(move |falsification| {
120 doc.text(format!("[{}]", index))
121 .append(doc.space().append(doc.text(show_debug(el))).nest(2))
122 .append(doc.space().append(doc.text("BECAUSE")))
123 .append(doc.space().append(falsification.group()).nest(2))
124 .append(doc.text(","))
125 .group()
126 })
127}
128
129fn fmt_falsifications<'d>(
130 i: impl Iterator<Item = DocBuilder<'d>>,
131 doc: &'d BoxAllocator,
132) -> Option<DocBuilder<'d>> {
133 i.take(MAX_ITER_FALSIFICATIONS).fold(None, |acc: Option<DocBuilder<'d>>, falsification| {
134 Some(acc.map_or_else(|| doc.nil(), |d| d.append(doc.space())).append(falsification))
135 })
136}
137
138pub trait IsAny<T> {
140 fn describe<'d>(&self, doc: &'d BoxAllocator) -> DocBuilder<'d>;
141 fn falsify_any<'d>(&self, t: &T, doc: &'d BoxAllocator) -> Option<DocBuilder<'d>>;
142}
143
144impl<T: 'static, Elem: Debug + 'static> IsAny<T> for Predicate<Elem>
145where
146 for<'a> &'a T: IntoIterator<Item = &'a Elem>,
147{
148 fn describe<'d>(&self, doc: &'d BoxAllocator) -> DocBuilder<'d> {
149 doc.text("ANY").append(doc.space().append(self.describe(doc)).nest(2)).group()
150 }
151 fn falsify_any<'d>(&self, t: &T, doc: &'d BoxAllocator) -> Option<DocBuilder<'d>> {
152 if t.into_iter().any(|e| self.satisfied(e)) {
153 None
154 } else {
155 fmt_falsifications(
157 t.into_iter().enumerate().filter_map(|(i, el)| falsify_elem(&self, i, &el, doc)),
158 doc,
159 )
160 .or_else(|| Some(doc.text("<empty>")))
161 }
162 }
163}
164
165pub trait IsAll<T> {
167 fn describe<'d>(&self, doc: &'d BoxAllocator) -> DocBuilder<'d>;
168 fn falsify_all<'d>(&self, t: &T, doc: &'d BoxAllocator) -> Option<DocBuilder<'d>>;
169}
170
171impl<T: 'static, Elem: Debug + 'static> IsAll<T> for Predicate<Elem>
172where
173 for<'a> &'a T: IntoIterator<Item = &'a Elem>,
174{
175 fn describe<'d>(&self, doc: &'d BoxAllocator) -> DocBuilder<'d> {
176 doc.text("ALL").append(doc.space().append(self.describe(doc)).nest(2)).group()
177 }
178
179 fn falsify_all<'d>(&self, t: &T, doc: &'d BoxAllocator) -> Option<DocBuilder<'d>> {
180 fmt_falsifications(
181 t.into_iter().enumerate().filter_map(|(i, el)| falsify_elem(&self, i, &el, doc)),
182 doc,
183 )
184 }
185}
186
187struct OverIterator<Elem>(Predicate<Elem>);
190
191impl<'e, Iter, Elem> IsAll<Iter> for OverIterator<Elem>
192where
193 Elem: Debug + 'static,
194 Iter: Iterator<Item = &'e Elem> + Clone,
195{
196 fn describe<'d>(&self, doc: &'d BoxAllocator) -> DocBuilder<'d> {
197 doc.text("ALL").append(doc.space().append(self.0.describe(doc)).nest(2)).group()
198 }
199
200 fn falsify_all<'d>(&self, iter: &Iter, doc: &'d BoxAllocator) -> Option<DocBuilder<'d>> {
201 fmt_falsifications(
202 iter.clone().enumerate().filter_map(|(i, el)| falsify_elem(&self.0, i, &el, doc)),
203 doc,
204 )
205 }
206}
207
208impl<'e, Iter, Elem> IsAny<Iter> for OverIterator<Elem>
209where
210 Elem: Debug + 'static,
211 Iter: Iterator<Item = &'e Elem> + Clone,
212{
213 fn describe<'d>(&self, doc: &'d BoxAllocator) -> DocBuilder<'d> {
214 doc.text("ANY").append(doc.space().append(self.0.describe(doc)).nest(2)).group()
215 }
216
217 fn falsify_any<'d>(&self, iter: &Iter, doc: &'d BoxAllocator) -> Option<DocBuilder<'d>> {
218 if iter.clone().any(|e| self.0.satisfied(e)) {
219 None
220 } else {
221 fmt_falsifications(
223 iter.clone().enumerate().filter_map(|(i, el)| falsify_elem(&self.0, i, &el, doc)),
224 doc,
225 )
226 .or_else(|| Some(doc.text("<empty>")))
227 }
228 }
229}
230
231enum OverPred<T, U> {
235 ByRef(Predicate<U>, Arc<dyn Fn(&T) -> &U + Send + Sync + 'static>, String),
236 ByValue(Predicate<U>, Arc<dyn Fn(&T) -> U + Send + Sync + 'static>, String),
237}
238
239pub trait IsOver<T> {
241 fn describe<'d>(&self, doc: &'d BoxAllocator) -> DocBuilder<'d>;
242 fn falsify_over<'d>(&self, t: &T, doc: &'d BoxAllocator) -> Option<DocBuilder<'d>>;
243}
244
245impl<T, U> IsOver<T> for OverPred<T, U> {
246 fn describe<'a>(&self, doc: &'a BoxAllocator) -> DocBuilder<'a> {
247 let (pred, path) = match self {
248 OverPred::ByRef(pred, _, path) => (pred, path),
249 OverPred::ByValue(pred, _, path) => (pred, path),
250 };
251 doc.as_string(path).append(doc.space()).append(pred.describe(doc)).group()
252 }
253 fn falsify_over<'d>(&self, t: &T, doc: &'d BoxAllocator) -> Option<DocBuilder<'d>> {
254 let (d, path) = match self {
255 OverPred::ByRef(pred, project, path) => (pred.falsify((project)(t), doc), path),
256 OverPred::ByValue(pred, project, path) => (pred.falsify(&(project)(t), doc), path),
257 };
258 d.map(|falsification| {
259 doc.as_string(path).append(doc.space().append(falsification).nest(2)).group()
260 })
261 }
262}
263
264impl<T> Predicate<T> {
265 fn is_equal(&self) -> bool {
266 matches!(self, Predicate::Equal(_, _, _))
267 }
268 pub fn describe<'a>(&self, doc: &'a BoxAllocator) -> DocBuilder<'a> {
269 match self {
270 Predicate::Equal(expected, _, repr) => {
271 doc.text("==").append(doc.space()).append(doc.text(repr(expected))).group()
272 }
273 Predicate::And(left, right) => parens(doc, left.describe(doc))
274 .append(doc.space())
275 .append(doc.text("AND"))
276 .append(doc.space())
277 .append(parens(doc, right.describe(doc)))
278 .group(),
279 Predicate::Or(left, right) => parens(doc, left.describe(doc))
280 .nest(2)
281 .append(doc.space())
282 .append(doc.text("OR"))
283 .append(doc.space())
284 .append(parens(doc, right.describe(doc)).nest(2))
285 .group(),
286 Predicate::Not(inner) if inner.is_equal() => {
288 if let Predicate::Equal(expected, _, repr) = &**inner {
289 doc.text("!=").append(doc.space()).append(doc.text(repr(&*expected))).group()
290 } else {
291 unreachable!()
293 }
294 }
295 Predicate::Not(inner) => {
296 doc.text("NOT").append(doc.space().append(inner.describe(doc)).nest(2)).group()
297 }
298 Predicate::Predicate(_, desc) => doc.as_string(desc).group(),
299 Predicate::Over(over) => over.describe(doc),
300 Predicate::Any(any) => any.describe(doc),
301 Predicate::All(all) => all.describe(doc),
302 }
303 }
304 pub fn falsify<'d>(&self, t: &T, doc: &'d BoxAllocator) -> Option<DocBuilder<'d>> {
306 match self {
307 Predicate::Equal(expected, are_eq, repr) => {
308 if are_eq(expected, t) {
309 None
310 } else {
311 Some(
312 doc.text(repr(t))
313 .append(doc.space())
314 .append(doc.text("!="))
315 .append(doc.space())
316 .append(doc.text(repr(expected)))
317 .group(),
318 )
319 }
320 }
321 Predicate::And(left, right) => match (left.falsify(t, doc), right.falsify(t, doc)) {
322 (Some(l), Some(r)) => Some(
323 parens(doc, l)
324 .append(doc.space())
325 .append(doc.text("AND"))
326 .append(doc.space())
327 .append(parens(doc, r))
328 .group(),
329 ),
330 (Some(l), None) => Some(l),
331 (None, Some(r)) => Some(r),
332 (None, None) => None,
333 },
334 Predicate::Or(left, right) => left
335 .falsify(t, doc)
336 .and_then(|l| right.falsify(t, doc).map(|r| parens(doc, l).append(parens(doc, r)))),
337 Predicate::Not(inner) => match inner.falsify(t, doc) {
338 Some(_) => None,
339 None => match &**inner {
340 Predicate::Equal(expected, _, repr) => Some(
341 doc.text(repr(t))
342 .append(doc.space())
343 .append(doc.text("=="))
344 .append(doc.space())
345 .append(doc.text(repr(&expected))),
346 ),
347 _ => Some(
348 doc.text("NOT")
349 .append(doc.space().append(inner.describe(doc)).nest(2))
350 .group(),
351 ),
352 },
353 },
354 Predicate::Predicate(pred, desc) => {
355 if pred(t) {
356 None
357 } else {
358 Some(doc.text("NOT").append(doc.space().append(doc.as_string(desc)).nest(2)))
359 }
360 }
361 Predicate::Over(over) => over.falsify_over(t, doc),
362 Predicate::Any(any) => any.falsify_any(t, doc),
363 Predicate::All(all) => all.falsify_all(t, doc),
364 }
365 }
366 pub fn satisfied<'t>(&self, t: &'t T) -> bool {
367 match self {
368 Predicate::Equal(expected, are_eq, _) => are_eq(t, expected),
369 Predicate::And(left, right) => left.satisfied(t) && right.satisfied(t),
370 Predicate::Or(left, right) => left.satisfied(t) || right.satisfied(t),
371 Predicate::Not(inner) => !inner.satisfied(t),
372 Predicate::Predicate(pred, _) => pred(t),
373 Predicate::Over(over) => over.falsify_over(t, &BoxAllocator).is_none(),
374 Predicate::Any(any) => any.falsify_any(t, &BoxAllocator).is_none(),
375 Predicate::All(all) => all.falsify_all(t, &BoxAllocator).is_none(),
376 }
377 }
378 pub fn assert_satisfied(&self, t: &T) -> Result<(), DebugString> {
379 let doc = BoxAllocator;
380 match self.falsify(t, &doc) {
381 Some(falsification) => {
382 let d = doc
383 .text("FAILED EXPECTATION")
384 .append(doc.newline().append(self.describe(&doc)).nest(2))
385 .append(doc.newline().append(doc.text("BECAUSE")))
386 .append(doc.newline().append(falsification).nest(2));
387 Err(DebugString(d.1.pretty(FMT_CHAR_WIDTH).to_string()))
388 }
389 None => Ok(()),
390 }
391 }
392 pub fn and(self, rhs: Predicate<T>) -> Predicate<T> {
393 Predicate::And(Box::new(self), Box::new(rhs))
394 }
395 pub fn or(self, rhs: Predicate<T>) -> Predicate<T> {
396 Predicate::Or(Box::new(self), Box::new(rhs))
397 }
398 pub fn not(self) -> Predicate<T> {
399 Predicate::Not(Box::new(self))
400 }
401 pub fn predicate<F, S>(f: F, label: S) -> Predicate<T>
403 where
404 F: for<'t> Fn(&'t T) -> bool + Send + Sync + 'static,
405 S: Into<String>,
406 {
407 Predicate::Predicate(Arc::new(f), label.into())
408 }
409}
410
411impl<T: PartialEq + Debug + 'static> Predicate<T> {
415 pub fn equal(t: T) -> Predicate<T> {
417 Predicate::Equal(Arc::new(t), Arc::new(T::eq), Arc::new(show_debug))
418 }
419 pub fn not_equal(t: T) -> Predicate<T> {
421 Predicate::Equal(Arc::new(t), Arc::new(T::eq), Arc::new(show_debug)).not()
422 }
423}
424
425impl<Elem, T> Predicate<T>
427where
428 T: Send + Sync + 'static,
429 for<'a> &'a T: IntoIterator<Item = &'a Elem>,
430 Elem: Debug + Send + Sync + 'static,
431{
432 pub fn all(pred: Predicate<Elem>) -> Predicate<T> {
435 Predicate::All(Arc::new(pred))
436 }
437
438 pub fn any(pred: Predicate<Elem>) -> Predicate<T> {
441 Predicate::Any(Arc::new(pred))
442 }
443}
444
445impl<'e, Elem, Iter> Predicate<Iter>
447where
448 Iter: Iterator<Item = &'e Elem> + Clone,
449 Elem: Debug + Send + Sync + 'static,
450{
451 pub fn iter_all(pred: Predicate<Elem>) -> Predicate<Iter> {
454 Predicate::All(Arc::new(OverIterator(pred)))
455 }
456
457 pub fn iter_any(pred: Predicate<Elem>) -> Predicate<Iter> {
460 Predicate::Any(Arc::new(OverIterator(pred)))
461 }
462}
463
464impl<U: Send + Sync + 'static> Predicate<U> {
465 pub fn over<F, T, P>(self, project: F, path: P) -> Predicate<T>
481 where
482 F: Fn(&T) -> &U + Send + Sync + 'static,
483 P: Into<String>,
484 T: 'static,
485 {
486 Predicate::Over(Arc::new(OverPred::ByRef(self, Arc::new(project), path.into())))
487 }
488
489 pub fn over_value<F, T, P>(self, project: F, path: P) -> Predicate<T>
504 where
505 F: Fn(&T) -> U + Send + Sync + 'static,
506 P: Into<String>,
507 T: 'static,
508 {
509 Predicate::Over(Arc::new(OverPred::ByValue(self, Arc::new(project), path.into())))
510 }
511}
512
513#[macro_export]
527macro_rules! over {
528 ($type:ty : $selector:tt, $pred:expr) => {
529 $pred.over(|var: &$type| &var.$selector, format!(".{}", stringify!($selector)))
530 };
531}
532
533#[macro_export]
543macro_rules! assert_satisfies {
544 ($subject:expr, $pred:expr) => {
545 if let Err(msg) = $pred.assert_satisfied($subject) {
546 panic!("{}", msg.0)
547 }
548 };
549}