Skip to content

Commit cc1dab0

Browse files
authored
Merge pull request #60 from epage/tree
A way to display *why* a predicate failed
2 parents b4659e5 + 67085fc commit cc1dab0

23 files changed

+1164
-43
lines changed

Cargo.toml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,9 @@ difference = { version = "2.0", optional = true }
2222
normalize-line-endings = { version = "0.2.2", optional = true }
2323
regex = { version="1.0", optional = true }
2424
float-cmp = { version="0.4", optional = true }
25+
treeline = { version = "0.1", optional = true }
2526

2627
[features]
27-
default = ["difference", "regex", "float-cmp", "normalize-line-endings"]
28+
default = ["difference", "regex", "float-cmp", "normalize-line-endings", "tree"]
2829
unstable = []
30+
tree = ["treeline",]

examples/case_tree.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
extern crate predicates;
2+
3+
use predicates::prelude::*;
4+
use predicates::tree::CaseTreeExt;
5+
6+
fn main() {
7+
let pred = predicate::ne(5).not().and(predicate::ge(5));
8+
9+
let var = 5;
10+
let case = pred.find_case(true, &var);
11+
if let Some(case) = case {
12+
println!("var is {}", var);
13+
println!("{}", case.tree());
14+
}
15+
}

src/boolean.rs

Lines changed: 254 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
use std::fmt;
1212
use std::marker::PhantomData;
1313

14+
use reflection;
1415
use Predicate;
1516

1617
/// Predicate that combines two `Predicate`s, returning the AND of the results.
@@ -53,6 +54,39 @@ where
5354
fn eval(&self, item: &Item) -> bool {
5455
self.a.eval(item) && self.b.eval(item)
5556
}
57+
58+
fn find_case<'a>(&'a self, expected: bool, variable: &Item) -> Option<reflection::Case<'a>> {
59+
let child_a = self.a.find_case(expected, variable);
60+
match (expected, child_a) {
61+
(true, Some(child_a)) => self.b.find_case(expected, variable).map(|child_b| {
62+
reflection::Case::new(Some(self), expected)
63+
.add_child(child_a)
64+
.add_child(child_b)
65+
}),
66+
(true, None) => None,
67+
(false, Some(child_a)) => {
68+
Some(reflection::Case::new(Some(self), expected).add_child(child_a))
69+
}
70+
(false, None) => self.b
71+
.find_case(expected, variable)
72+
.map(|child_b| reflection::Case::new(Some(self), expected).add_child(child_b)),
73+
}
74+
}
75+
}
76+
77+
impl<M1, M2, Item> reflection::PredicateReflection for AndPredicate<M1, M2, Item>
78+
where
79+
M1: Predicate<Item>,
80+
M2: Predicate<Item>,
81+
Item: ?Sized,
82+
{
83+
fn children<'a>(&'a self) -> Box<Iterator<Item = reflection::Child<'a>> + 'a> {
84+
let params = vec![
85+
reflection::Child::new("left", &self.a),
86+
reflection::Child::new("right", &self.b),
87+
];
88+
Box::new(params.into_iter())
89+
}
5690
}
5791

5892
impl<M1, M2, Item> fmt::Display for AndPredicate<M1, M2, Item>
@@ -66,6 +100,91 @@ where
66100
}
67101
}
68102

103+
#[cfg(test)]
104+
mod test_and {
105+
use prelude::*;
106+
107+
#[test]
108+
fn find_case_true() {
109+
assert!(
110+
predicate::always()
111+
.and(predicate::always())
112+
.find_case(true, &5)
113+
.is_some()
114+
);
115+
}
116+
117+
#[test]
118+
fn find_case_true_left_fail() {
119+
assert!(
120+
predicate::never()
121+
.and(predicate::always())
122+
.find_case(true, &5)
123+
.is_none()
124+
);
125+
}
126+
127+
#[test]
128+
fn find_case_true_right_fail() {
129+
assert!(
130+
predicate::always()
131+
.and(predicate::never())
132+
.find_case(true, &5)
133+
.is_none()
134+
);
135+
}
136+
137+
#[test]
138+
fn find_case_true_fails() {
139+
assert!(
140+
predicate::never()
141+
.and(predicate::never())
142+
.find_case(true, &5)
143+
.is_none()
144+
);
145+
}
146+
147+
#[test]
148+
fn find_case_false() {
149+
assert!(
150+
predicate::never()
151+
.and(predicate::never())
152+
.find_case(false, &5)
153+
.is_some()
154+
);
155+
}
156+
157+
#[test]
158+
fn find_case_false_fails() {
159+
assert!(
160+
predicate::always()
161+
.and(predicate::always())
162+
.find_case(false, &5)
163+
.is_none()
164+
);
165+
}
166+
167+
#[test]
168+
fn find_case_false_left_fail() {
169+
assert!(
170+
predicate::never()
171+
.and(predicate::always())
172+
.find_case(false, &5)
173+
.is_some()
174+
);
175+
}
176+
177+
#[test]
178+
fn find_case_false_right_fail() {
179+
assert!(
180+
predicate::always()
181+
.and(predicate::never())
182+
.find_case(false, &5)
183+
.is_some()
184+
);
185+
}
186+
}
187+
69188
/// Predicate that combines two `Predicate`s, returning the OR of the results.
70189
///
71190
/// This is created by the `Predicate::or` function.
@@ -106,6 +225,39 @@ where
106225
fn eval(&self, item: &Item) -> bool {
107226
self.a.eval(item) || self.b.eval(item)
108227
}
228+
229+
fn find_case<'a>(&'a self, expected: bool, variable: &Item) -> Option<reflection::Case<'a>> {
230+
let child_a = self.a.find_case(expected, variable);
231+
match (expected, child_a) {
232+
(true, Some(child_a)) => {
233+
Some(reflection::Case::new(Some(self), expected).add_child(child_a))
234+
}
235+
(true, None) => self.b
236+
.find_case(expected, variable)
237+
.map(|child_b| reflection::Case::new(Some(self), expected).add_child(child_b)),
238+
(false, Some(child_a)) => self.b.find_case(expected, variable).map(|child_b| {
239+
reflection::Case::new(Some(self), expected)
240+
.add_child(child_a)
241+
.add_child(child_b)
242+
}),
243+
(false, None) => None,
244+
}
245+
}
246+
}
247+
248+
impl<M1, M2, Item> reflection::PredicateReflection for OrPredicate<M1, M2, Item>
249+
where
250+
M1: Predicate<Item>,
251+
M2: Predicate<Item>,
252+
Item: ?Sized,
253+
{
254+
fn children<'a>(&'a self) -> Box<Iterator<Item = reflection::Child<'a>> + 'a> {
255+
let params = vec![
256+
reflection::Child::new("left", &self.a),
257+
reflection::Child::new("right", &self.b),
258+
];
259+
Box::new(params.into_iter())
260+
}
109261
}
110262

111263
impl<M1, M2, Item> fmt::Display for OrPredicate<M1, M2, Item>
@@ -119,6 +271,91 @@ where
119271
}
120272
}
121273

274+
#[cfg(test)]
275+
mod test_or {
276+
use prelude::*;
277+
278+
#[test]
279+
fn find_case_true() {
280+
assert!(
281+
predicate::always()
282+
.or(predicate::always())
283+
.find_case(true, &5)
284+
.is_some()
285+
);
286+
}
287+
288+
#[test]
289+
fn find_case_true_left_fail() {
290+
assert!(
291+
predicate::never()
292+
.or(predicate::always())
293+
.find_case(true, &5)
294+
.is_some()
295+
);
296+
}
297+
298+
#[test]
299+
fn find_case_true_right_fail() {
300+
assert!(
301+
predicate::always()
302+
.or(predicate::never())
303+
.find_case(true, &5)
304+
.is_some()
305+
);
306+
}
307+
308+
#[test]
309+
fn find_case_true_fails() {
310+
assert!(
311+
predicate::never()
312+
.or(predicate::never())
313+
.find_case(true, &5)
314+
.is_none()
315+
);
316+
}
317+
318+
#[test]
319+
fn find_case_false() {
320+
assert!(
321+
predicate::never()
322+
.or(predicate::never())
323+
.find_case(false, &5)
324+
.is_some()
325+
);
326+
}
327+
328+
#[test]
329+
fn find_case_false_fails() {
330+
assert!(
331+
predicate::always()
332+
.or(predicate::always())
333+
.find_case(false, &5)
334+
.is_none()
335+
);
336+
}
337+
338+
#[test]
339+
fn find_case_false_left_fail() {
340+
assert!(
341+
predicate::never()
342+
.or(predicate::always())
343+
.find_case(false, &5)
344+
.is_none()
345+
);
346+
}
347+
348+
#[test]
349+
fn find_case_false_right_fail() {
350+
assert!(
351+
predicate::always()
352+
.or(predicate::never())
353+
.find_case(false, &5)
354+
.is_none()
355+
);
356+
}
357+
}
358+
122359
/// Predicate that returns a `Predicate` taking the logical NOT of the result.
123360
///
124361
/// This is created by the `Predicate::not` function.
@@ -154,6 +391,23 @@ where
154391
fn eval(&self, item: &Item) -> bool {
155392
!self.inner.eval(item)
156393
}
394+
395+
fn find_case<'a>(&'a self, expected: bool, variable: &Item) -> Option<reflection::Case<'a>> {
396+
self.inner
397+
.find_case(!expected, variable)
398+
.map(|child| reflection::Case::new(Some(self), expected).add_child(child))
399+
}
400+
}
401+
402+
impl<M, Item> reflection::PredicateReflection for NotPredicate<M, Item>
403+
where
404+
M: Predicate<Item>,
405+
Item: ?Sized,
406+
{
407+
fn children<'a>(&'a self) -> Box<Iterator<Item = reflection::Child<'a>> + 'a> {
408+
let params = vec![reflection::Child::new("predicate", &self.inner)];
409+
Box::new(params.into_iter())
410+
}
157411
}
158412

159413
impl<M, Item> fmt::Display for NotPredicate<M, Item>

src/boxed.rs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@
1111
1212
use std::fmt;
1313

14+
use core;
15+
use reflection;
1416
use Predicate;
1517

1618
/// `Predicate` that wraps another `Predicate` as a trait object, allowing
@@ -40,6 +42,19 @@ where
4042
}
4143
}
4244

45+
impl<Item> reflection::PredicateReflection for BoxPredicate<Item>
46+
where
47+
Item: ?Sized,
48+
{
49+
fn parameters<'a>(&'a self) -> Box<Iterator<Item = reflection::Parameter<'a>> + 'a> {
50+
self.0.parameters()
51+
}
52+
53+
fn children<'a>(&'a self) -> Box<Iterator<Item = reflection::Child<'a>> + 'a> {
54+
self.0.children()
55+
}
56+
}
57+
4358
impl<Item> fmt::Display for BoxPredicate<Item>
4459
where
4560
Item: ?Sized,
@@ -56,6 +71,10 @@ where
5671
fn eval(&self, variable: &Item) -> bool {
5772
self.0.eval(variable)
5873
}
74+
75+
fn find_case<'a>(&'a self, expected: bool, variable: &Item) -> Option<reflection::Case<'a>> {
76+
core::default_find_case(self, expected, variable)
77+
}
5978
}
6079

6180
/// `Predicate` extension for boxing a `Predicate`.

src/constant.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@
1111
use std::fmt;
1212
use std::marker::PhantomData;
1313

14+
use core;
15+
use reflection;
1416
use Predicate;
1517

1618
/// Predicate that always returns a constant (boolean) result.
@@ -26,6 +28,17 @@ impl<Item> Predicate<Item> for BooleanPredicate<Item> {
2628
fn eval(&self, _variable: &Item) -> bool {
2729
self.retval
2830
}
31+
32+
fn find_case<'a>(&'a self, expected: bool, variable: &Item) -> Option<reflection::Case<'a>> {
33+
core::default_find_case(self, expected, variable)
34+
}
35+
}
36+
37+
impl<Item> reflection::PredicateReflection for BooleanPredicate<Item> {
38+
fn parameters<'a>(&'a self) -> Box<Iterator<Item = reflection::Parameter<'a>> + 'a> {
39+
let params = vec![reflection::Parameter::new("value", &self.retval)];
40+
Box::new(params.into_iter())
41+
}
2942
}
3043

3144
impl<Item> fmt::Display for BooleanPredicate<Item> {

0 commit comments

Comments
 (0)