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(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 if let Predicate::Equal(_, _, _) = self {
267 true
268 } else {
269 false
270 }
271 }
272 pub fn describe<'a>(&self, doc: &'a BoxAllocator) -> DocBuilder<'a> {
273 match self {
274 Predicate::Equal(expected, _, repr) => {
275 doc.text("==").append(doc.space()).append(doc.text(repr(expected))).group()
276 }
277 Predicate::And(left, right) => parens(doc, left.describe(doc))
278 .append(doc.space())
279 .append(doc.text("AND"))
280 .append(doc.space())
281 .append(parens(doc, right.describe(doc)))
282 .group(),
283 Predicate::Or(left, right) => parens(doc, left.describe(doc))
284 .nest(2)
285 .append(doc.space())
286 .append(doc.text("OR"))
287 .append(doc.space())
288 .append(parens(doc, right.describe(doc)).nest(2))
289 .group(),
290 Predicate::Not(inner) if inner.is_equal() => {
292 if let Predicate::Equal(expected, _, repr) = &**inner {
293 doc.text("!=").append(doc.space()).append(doc.text(repr(&*expected))).group()
294 } else {
295 unreachable!()
297 }
298 }
299 Predicate::Not(inner) => {
300 doc.text("NOT").append(doc.space().append(inner.describe(doc)).nest(2)).group()
301 }
302 Predicate::Predicate(_, desc) => doc.as_string(desc).group(),
303 Predicate::Over(over) => over.describe(doc),
304 Predicate::Any(any) => any.describe(doc),
305 Predicate::All(all) => all.describe(doc),
306 }
307 }
308 pub fn falsify<'d>(&self, t: &T, doc: &'d BoxAllocator) -> Option<DocBuilder<'d>> {
310 match self {
311 Predicate::Equal(expected, are_eq, repr) => {
312 if are_eq(expected, t) {
313 None
314 } else {
315 Some(
316 doc.text(repr(t))
317 .append(doc.space())
318 .append(doc.text("!="))
319 .append(doc.space())
320 .append(doc.text(repr(expected)))
321 .group(),
322 )
323 }
324 }
325 Predicate::And(left, right) => match (left.falsify(t, doc), right.falsify(t, doc)) {
326 (Some(l), Some(r)) => Some(
327 parens(doc, l)
328 .append(doc.space())
329 .append(doc.text("AND"))
330 .append(doc.space())
331 .append(parens(doc, r))
332 .group(),
333 ),
334 (Some(l), None) => Some(l),
335 (None, Some(r)) => Some(r),
336 (None, None) => None,
337 },
338 Predicate::Or(left, right) => left
339 .falsify(t, doc)
340 .and_then(|l| right.falsify(t, doc).map(|r| parens(doc, l).append(parens(doc, r)))),
341 Predicate::Not(inner) => match inner.falsify(t, doc) {
342 Some(_) => None,
343 None => match &**inner {
344 Predicate::Equal(expected, _, repr) => Some(
345 doc.text(repr(t))
346 .append(doc.space())
347 .append(doc.text("=="))
348 .append(doc.space())
349 .append(doc.text(repr(&expected))),
350 ),
351 _ => Some(
352 doc.text("NOT")
353 .append(doc.space().append(inner.describe(doc)).nest(2))
354 .group(),
355 ),
356 },
357 },
358 Predicate::Predicate(pred, desc) => {
359 if pred(t) {
360 None
361 } else {
362 Some(doc.text("NOT").append(doc.space().append(doc.as_string(desc)).nest(2)))
363 }
364 }
365 Predicate::Over(over) => over.falsify_over(t, doc),
366 Predicate::Any(any) => any.falsify_any(t, doc),
367 Predicate::All(all) => all.falsify_all(t, doc),
368 }
369 }
370 pub fn satisfied<'t>(&self, t: &'t T) -> bool {
371 match self {
372 Predicate::Equal(expected, are_eq, _) => are_eq(t, expected),
373 Predicate::And(left, right) => left.satisfied(t) && right.satisfied(t),
374 Predicate::Or(left, right) => left.satisfied(t) || right.satisfied(t),
375 Predicate::Not(inner) => !inner.satisfied(t),
376 Predicate::Predicate(pred, _) => pred(t),
377 Predicate::Over(over) => over.falsify_over(t, &BoxAllocator).is_none(),
378 Predicate::Any(any) => any.falsify_any(t, &BoxAllocator).is_none(),
379 Predicate::All(all) => all.falsify_all(t, &BoxAllocator).is_none(),
380 }
381 }
382 pub fn assert_satisfied(&self, t: &T) -> Result<(), DebugString> {
383 let doc = BoxAllocator;
384 match self.falsify(t, &doc) {
385 Some(falsification) => {
386 let d = doc
387 .text("FAILED EXPECTATION")
388 .append(doc.newline().append(self.describe(&doc)).nest(2))
389 .append(doc.newline().append(doc.text("BECAUSE")))
390 .append(doc.newline().append(falsification).nest(2));
391 Err(DebugString(d.1.pretty(FMT_CHAR_WIDTH).to_string()))
392 }
393 None => Ok(()),
394 }
395 }
396 pub fn and(self, rhs: Predicate<T>) -> Predicate<T> {
397 Predicate::And(Box::new(self), Box::new(rhs))
398 }
399 pub fn or(self, rhs: Predicate<T>) -> Predicate<T> {
400 Predicate::Or(Box::new(self), Box::new(rhs))
401 }
402 pub fn not(self) -> Predicate<T> {
403 Predicate::Not(Box::new(self))
404 }
405 pub fn predicate<F, S>(f: F, label: S) -> Predicate<T>
407 where
408 F: for<'t> Fn(&'t T) -> bool + Send + Sync + 'static,
409 S: Into<String>,
410 {
411 Predicate::Predicate(Arc::new(f), label.into())
412 }
413}
414
415impl<T: PartialEq + Debug + 'static> Predicate<T> {
419 pub fn equal(t: T) -> Predicate<T> {
421 Predicate::Equal(Arc::new(t), Arc::new(T::eq), Arc::new(show_debug))
422 }
423 pub fn not_equal(t: T) -> Predicate<T> {
425 Predicate::Equal(Arc::new(t), Arc::new(T::eq), Arc::new(show_debug)).not()
426 }
427}
428
429impl<Elem, T> Predicate<T>
431where
432 T: Send + Sync + 'static,
433 for<'a> &'a T: IntoIterator<Item = &'a Elem>,
434 Elem: Debug + Send + Sync + 'static,
435{
436 pub fn all(pred: Predicate<Elem>) -> Predicate<T> {
439 Predicate::All(Arc::new(pred))
440 }
441
442 pub fn any(pred: Predicate<Elem>) -> Predicate<T> {
445 Predicate::Any(Arc::new(pred))
446 }
447}
448
449impl<'e, Elem, Iter> Predicate<Iter>
451where
452 Iter: Iterator<Item = &'e Elem> + Clone,
453 Elem: Debug + Send + Sync + 'static,
454{
455 pub fn iter_all(pred: Predicate<Elem>) -> Predicate<Iter> {
458 Predicate::All(Arc::new(OverIterator(pred)))
459 }
460
461 pub fn iter_any(pred: Predicate<Elem>) -> Predicate<Iter> {
464 Predicate::Any(Arc::new(OverIterator(pred)))
465 }
466}
467
468impl<U: Send + Sync + 'static> Predicate<U> {
469 pub fn over<F, T, P>(self, project: F, path: P) -> Predicate<T>
485 where
486 F: Fn(&T) -> &U + Send + Sync + 'static,
487 P: Into<String>,
488 T: 'static,
489 {
490 Predicate::Over(Arc::new(OverPred::ByRef(self, Arc::new(project), path.into())))
491 }
492
493 pub fn over_value<F, T, P>(self, project: F, path: P) -> Predicate<T>
508 where
509 F: Fn(&T) -> U + Send + Sync + 'static,
510 P: Into<String>,
511 T: 'static,
512 {
513 Predicate::Over(Arc::new(OverPred::ByValue(self, Arc::new(project), path.into())))
514 }
515}
516
517#[macro_export]
531macro_rules! over {
532 ($type:ty : $selector:tt, $pred:expr) => {
533 $pred.over(|var: &$type| &var.$selector, format!(".{}", stringify!($selector)))
534 };
535}
536
537#[macro_export]
547macro_rules! assert_satisfies {
548 ($subject:expr, $pred:expr) => {
549 if let Err(msg) = $pred.assert_satisfied($subject) {
550 panic!("{}", msg.0)
551 }
552 };
553}