Skip to content

Commit 5d6f8da

Browse files
committed
feat: Implement find_case for predicates
A non-default implementation is provided for all predicates that can add additional information than what `eval` / `Display` provide. Exceptions - `eq_file`: I want to re-work this predicate - `BoxPredicate`: The where clause is causing problems This is the information needed for resolving #7. Now, rendering is the only thing left.
1 parent cadd280 commit 5d6f8da

File tree

9 files changed

+362
-3
lines changed

9 files changed

+362
-3
lines changed

src/boolean.rs

Lines changed: 212 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,24 @@ where
5454
fn eval(&self, item: &Item) -> bool {
5555
self.a.eval(item) && self.b.eval(item)
5656
}
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+
}
5775
}
5876

5977
impl<M1, M2, Item> reflection::PredicateReflection for AndPredicate<M1, M2, Item>
@@ -82,6 +100,91 @@ where
82100
}
83101
}
84102

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+
85188
/// Predicate that combines two `Predicate`s, returning the OR of the results.
86189
///
87190
/// This is created by the `Predicate::or` function.
@@ -122,6 +225,24 @@ where
122225
fn eval(&self, item: &Item) -> bool {
123226
self.a.eval(item) || self.b.eval(item)
124227
}
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+
}
125246
}
126247

127248
impl<M1, M2, Item> reflection::PredicateReflection for OrPredicate<M1, M2, Item>
@@ -150,6 +271,91 @@ where
150271
}
151272
}
152273

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+
153359
/// Predicate that returns a `Predicate` taking the logical NOT of the result.
154360
///
155361
/// This is created by the `Predicate::not` function.
@@ -185,6 +391,12 @@ where
185391
fn eval(&self, item: &Item) -> bool {
186392
!self.inner.eval(item)
187393
}
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+
}
188400
}
189401

190402
impl<M, Item> reflection::PredicateReflection for NotPredicate<M, Item>

src/float/close.rs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,25 @@ impl Predicate<f64> for IsClosePredicate {
8585
fn eval(&self, variable: &f64) -> bool {
8686
variable.approx_eq(&self.target, self.epsilon, self.ulps)
8787
}
88+
89+
fn find_case<'a>(&'a self, expected: bool, variable: &f64) -> Option<reflection::Case<'a>> {
90+
let actual = self.eval(variable);
91+
if expected == actual {
92+
Some(
93+
reflection::Case::new(Some(self), actual)
94+
.add_product(reflection::Product::new(
95+
"actual epsilon",
96+
(variable - self.target).abs(),
97+
))
98+
.add_product(reflection::Product::new(
99+
"actual ulps",
100+
variable.ulps(&self.target).abs(),
101+
)),
102+
)
103+
} else {
104+
None
105+
}
106+
}
88107
}
89108

90109
impl reflection::PredicateReflection for IsClosePredicate {

src/name.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,10 @@ where
3636
fn eval(&self, item: &Item) -> bool {
3737
self.inner.eval(item)
3838
}
39+
40+
fn find_case<'a>(&'a self, expected: bool, variable: &Item) -> Option<reflection::Case<'a>> {
41+
self.inner.find_case(expected, variable)
42+
}
3943
}
4044

4145
impl<M, Item> reflection::PredicateReflection for NamePredicate<M, Item>

src/path/fc.rs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,24 @@ where
6767
fn eval(&self, path: &path::Path) -> bool {
6868
self.eval(path).unwrap_or(false)
6969
}
70+
71+
fn find_case<'a>(
72+
&'a self,
73+
expected: bool,
74+
variable: &path::Path,
75+
) -> Option<reflection::Case<'a>> {
76+
let buffer = read_file(variable);
77+
match (expected, buffer) {
78+
(_, Ok(buffer)) => self.p
79+
.find_case(expected, &buffer)
80+
.map(|child| reflection::Case::new(Some(self), expected).add_child(child)),
81+
(true, Err(_)) => None,
82+
(false, Err(err)) => Some(
83+
reflection::Case::new(Some(self), false)
84+
.add_product(reflection::Product::new("error", err)),
85+
),
86+
}
87+
}
7088
}
7189

7290
/// `Predicate` extension adapting a `slice` Predicate.

src/path/ft.rs

Lines changed: 33 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,12 @@ enum FileType {
2222
}
2323

2424
impl FileType {
25-
fn from_path(path: &path::Path) -> io::Result<FileType> {
26-
let file_type = path.metadata()?.file_type();
25+
fn from_path(path: &path::Path, follow: bool) -> io::Result<FileType> {
26+
let file_type = if follow {
27+
path.metadata()
28+
} else {
29+
path.symlink_metadata()
30+
}?.file_type();
2731
if file_type.is_dir() {
2832
return Ok(FileType::Dir);
2933
}
@@ -76,7 +80,7 @@ impl FileTypePredicate {
7680
/// Allow to create an `FileTypePredicate` from a `path`
7781
pub fn from_path(path: &path::Path) -> io::Result<FileTypePredicate> {
7882
Ok(FileTypePredicate {
79-
ft: FileType::from_path(path)?,
83+
ft: FileType::from_path(path, true)?,
8084
follow: true,
8185
})
8286
}
@@ -93,6 +97,32 @@ impl Predicate<path::Path> for FileTypePredicate {
9397
.map(|m| self.ft.eval(m.file_type()))
9498
.unwrap_or(false)
9599
}
100+
101+
fn find_case<'a>(
102+
&'a self,
103+
expected: bool,
104+
variable: &path::Path,
105+
) -> Option<reflection::Case<'a>> {
106+
let actual_type = FileType::from_path(variable, self.follow);
107+
match (expected, actual_type) {
108+
(_, Ok(actual_type)) => {
109+
let result = self.ft == actual_type;
110+
if result == expected {
111+
Some(
112+
reflection::Case::new(Some(self), result)
113+
.add_product(reflection::Product::new("actual filetype", actual_type)),
114+
)
115+
} else {
116+
None
117+
}
118+
}
119+
(true, Err(_)) => None,
120+
(false, Err(err)) => Some(
121+
reflection::Case::new(Some(self), false)
122+
.add_product(reflection::Product::new("error", err)),
123+
),
124+
}
125+
}
96126
}
97127

98128
impl reflection::PredicateReflection for FileTypePredicate {

0 commit comments

Comments
 (0)