Skip to content

Commit 083fe5d

Browse files
authored
Merge pull request #18 from epage/pred
Add specialized predicates
2 parents d65bf5b + 0a6d632 commit 083fe5d

File tree

12 files changed

+549
-1
lines changed

12 files changed

+549
-1
lines changed

.travis.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ install:
3030
- cargo -V
3131

3232
script:
33+
- cargo check --verbose --no-default-features
3334
- cargo check --verbose
3435
- cargo test --verbose
3536
- cargo doc --no-deps

Cargo.toml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,10 @@ travis-ci = { repository = "assert-rs/predicates-rs" }
1818
appveyor = { repository = "assert-rs/predicates-rs" }
1919

2020
[dependencies]
21+
difference = { version = "2.0", optional = true }
22+
regex = { version="0.2", optional = true }
23+
float-cmp = { version="0.4", optional = true }
2124

2225
[features]
23-
default = []
26+
default = ["difference", "regex", "float-cmp"]
2427
unstable = []

src/lib.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,13 @@
8585
8686
#![deny(missing_docs, missing_debug_implementations)]
8787

88+
#[cfg(feature = "difference")]
89+
extern crate difference;
90+
#[cfg(feature = "float-cmp")]
91+
extern crate float_cmp;
92+
#[cfg(feature = "regex")]
93+
extern crate regex;
94+
8895
// core `Predicate` trait
8996
pub mod predicate;
9097
pub use self::predicate::{BoxPredicate, Predicate};

src/predicate/float/close.rs

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
// Copyright (c) 2018 The predicates-rs Project Developers.
2+
//
3+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
4+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
5+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
6+
// option. This file may not be copied, modified, or distributed
7+
// except according to those terms.
8+
9+
use float_cmp::ApproxEq;
10+
use float_cmp::Ulps;
11+
12+
use Predicate;
13+
14+
/// Predicate that ensures two numbers are "close" enough, understanding that rounding errors
15+
/// occur.
16+
///
17+
/// This is created by the `predicate::float::is_close`.
18+
#[derive(Clone, Debug)]
19+
pub struct IsClosePredicate {
20+
target: f64,
21+
epsilon: f64,
22+
ulps: <f64 as Ulps>::U,
23+
}
24+
25+
impl IsClosePredicate {
26+
/// Set the amount of error allowed.
27+
///
28+
/// Values `1`-`5` should work in most cases. Sometimes more control is needed and you will
29+
/// need to set `IsClosePredicate::epsilon` separately from `IsClosePredicate::ulps`.
30+
///
31+
/// # Examples
32+
///
33+
/// ```
34+
/// use predicates::predicate::*;
35+
///
36+
/// let a = 0.15_f64 + 0.15_f64 + 0.15_f64;
37+
/// let predicate_fn = float::is_close(a).distance(5);
38+
/// ```
39+
pub fn distance(mut self, distance: <f64 as Ulps>::U) -> Self {
40+
self.epsilon = (distance as f64) * ::std::f64::EPSILON;
41+
self.ulps = distance;
42+
self
43+
}
44+
45+
/// Set the absolute deviation allowed.
46+
///
47+
/// This is meant to handle problems near `0`. Values `1.`-`5.` epislons should work in most
48+
/// cases.
49+
///
50+
/// # Examples
51+
///
52+
/// ```
53+
/// use predicates::predicate::*;
54+
///
55+
/// let a = 0.15_f64 + 0.15_f64 + 0.15_f64;
56+
/// let predicate_fn = float::is_close(a).epsilon(5.0 * ::std::f64::EPSILON);
57+
/// ```
58+
pub fn epsilon(mut self, epsilon: f64) -> Self {
59+
self.epsilon = epsilon;
60+
self
61+
}
62+
63+
/// Set the relative deviation allowed.
64+
///
65+
/// This is meant to handle large numbers. Values `1`-`5` should work in most cases.
66+
///
67+
/// # Examples
68+
///
69+
/// ```
70+
/// use predicates::predicate::*;
71+
///
72+
/// let a = 0.15_f64 + 0.15_f64 + 0.15_f64;
73+
/// let predicate_fn = float::is_close(a).ulps(5);
74+
/// ```
75+
pub fn ulps(mut self, ulps: <f64 as Ulps>::U) -> Self {
76+
self.ulps = ulps;
77+
self
78+
}
79+
}
80+
81+
impl Predicate for IsClosePredicate {
82+
type Item = f64;
83+
84+
fn eval(&self, variable: &f64) -> bool {
85+
variable.approx_eq(&self.target, self.epsilon, self.ulps)
86+
}
87+
}
88+
89+
/// Create a new `Predicate` that ensures two numbers are "close" enough, understanding that
90+
/// rounding errors occur.
91+
///
92+
/// # Examples
93+
///
94+
/// ```
95+
/// use predicates::predicate::*;
96+
///
97+
/// let a = 0.15_f64 + 0.15_f64 + 0.15_f64;
98+
/// let b = 0.1_f64 + 0.1_f64 + 0.25_f64;
99+
/// let predicate_fn = float::is_close(a);
100+
/// assert_eq!(true, predicate_fn.eval(&b));
101+
/// assert_eq!(false, predicate_fn.distance(0).eval(&b));
102+
/// ```
103+
pub fn is_close(target: f64) -> IsClosePredicate {
104+
IsClosePredicate {
105+
target,
106+
epsilon: 2.0 * ::std::f64::EPSILON,
107+
ulps: 2,
108+
}
109+
}

src/predicate/float/mod.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// Copyright (c) 2018 The predicates-rs Project Developers.
2+
//
3+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
4+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
5+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
6+
// option. This file may not be copied, modified, or distributed
7+
// except according to those terms.
8+
9+
//! Float Predicates
10+
//!
11+
//! This module contains predicates specific to string handling.
12+
13+
#[cfg(feature = "float-cmp")]
14+
mod close;
15+
#[cfg(feature = "float-cmp")]
16+
pub use self::close::{is_close, IsClosePredicate};

src/predicate/mod.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,11 @@ pub use self::ord::{eq, ge, gt, le, lt, ne, EqPredicate, OrdPredicate};
2222
pub use self::set::{contains, contains_hashable, contains_ord, ContainsPredicate,
2323
HashableContainsPredicate, OrdContainsPredicate};
2424

25+
// specialized primitive `Predicate` types
26+
pub mod str;
27+
pub mod path;
28+
pub mod float;
29+
2530
// combinators
2631
mod boolean;
2732
mod boxed;

src/predicate/path/existence.rs

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
// Copyright (c) 2018 The predicates-rs Project Developers.
2+
//
3+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
4+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
5+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
6+
// option. This file may not be copied, modified, or distributed
7+
// except according to those terms.
8+
9+
use std::path;
10+
11+
use Predicate;
12+
13+
/// Predicate that checks if a file is present
14+
///
15+
/// This is created by the `predicate::path::exists` and `predicate::path::missing`.
16+
#[derive(Debug)]
17+
pub struct ExistencePredicate {
18+
exists: bool,
19+
}
20+
21+
impl Predicate for ExistencePredicate {
22+
type Item = path::Path;
23+
24+
fn eval(&self, path: &path::Path) -> bool {
25+
path.exists() == self.exists
26+
}
27+
}
28+
29+
/// Creates a new `Predicate` that ensures the path exists.
30+
///
31+
/// # Examples
32+
///
33+
/// ```
34+
/// use std::path::Path;
35+
/// use predicates::predicate::*;
36+
///
37+
/// let predicate_fn = path::exists();
38+
/// assert_eq!(true, predicate_fn.eval(Path::new("Cargo.toml")));
39+
/// ```
40+
pub fn exists() -> ExistencePredicate {
41+
ExistencePredicate { exists: true }
42+
}
43+
44+
/// Creates a new `Predicate` that ensures the path doesn't exist.
45+
///
46+
/// # Examples
47+
///
48+
/// ```
49+
/// use std::path::Path;
50+
/// use predicates::predicate::*;
51+
///
52+
/// let predicate_fn = path::missing();
53+
/// assert_eq!(true, predicate_fn.eval(Path::new("non-existent-file.foo")));
54+
/// ```
55+
pub fn missing() -> ExistencePredicate {
56+
ExistencePredicate { exists: false }
57+
}

src/predicate/path/ft.rs

Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
// Copyright (c) 2018 The predicates-rs Project Developers.
2+
//
3+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
4+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
5+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
6+
// option. This file may not be copied, modified, or distributed
7+
// except according to those terms.
8+
9+
use std::path;
10+
use std::fs;
11+
12+
use Predicate;
13+
14+
#[derive(Clone, Copy, Debug)]
15+
enum FileType {
16+
File,
17+
Dir,
18+
Symlink,
19+
}
20+
21+
impl FileType {
22+
fn eval(self, ft: &fs::FileType) -> bool {
23+
match self {
24+
FileType::File => ft.is_file(),
25+
FileType::Dir => ft.is_dir(),
26+
FileType::Symlink => ft.is_symlink(),
27+
}
28+
}
29+
}
30+
31+
/// Predicate that checks the `std::fs::FileType`.
32+
///
33+
/// This is created by the `predicate::path::is_file`, `predicate::path::is_dir`, and `predicate::path::is_symlink`.
34+
#[derive(Debug)]
35+
pub struct FileTypePredicate {
36+
ft: FileType,
37+
follow: bool,
38+
}
39+
40+
impl FileTypePredicate {
41+
/// Follow symbolic links.
42+
///
43+
/// When yes is true, symbolic links are followed as if they were normal directories and files.
44+
///
45+
/// Default: disabled.
46+
pub fn follow_links(mut self, yes: bool) -> Self {
47+
self.follow = yes;
48+
self
49+
}
50+
}
51+
52+
impl Predicate for FileTypePredicate {
53+
type Item = path::Path;
54+
55+
fn eval(&self, path: &path::Path) -> bool {
56+
let metadata = if self.follow {
57+
path.metadata()
58+
} else {
59+
path.symlink_metadata()
60+
};
61+
metadata
62+
.map(|m| self.ft.eval(&m.file_type()))
63+
.unwrap_or(false)
64+
}
65+
}
66+
67+
/// Creates a new `Predicate` that ensures the path points to a file.
68+
///
69+
/// # Examples
70+
///
71+
/// ```
72+
/// use std::path::Path;
73+
/// use predicates::predicate::*;
74+
///
75+
/// let predicate_fn = path::is_file();
76+
/// assert_eq!(true, predicate_fn.eval(Path::new("Cargo.toml")));
77+
/// assert_eq!(false, predicate_fn.eval(Path::new("src")));
78+
/// assert_eq!(false, predicate_fn.eval(Path::new("non-existent-file.foo")));
79+
/// ```
80+
pub fn is_file() -> FileTypePredicate {
81+
FileTypePredicate {
82+
ft: FileType::File,
83+
follow: false,
84+
}
85+
}
86+
87+
/// Creates a new `Predicate` that ensures the path points to a directory.
88+
///
89+
/// # Examples
90+
///
91+
/// ```
92+
/// use std::path::Path;
93+
/// use predicates::predicate::*;
94+
///
95+
/// let predicate_fn = path::is_dir();
96+
/// assert_eq!(false, predicate_fn.eval(Path::new("Cargo.toml")));
97+
/// assert_eq!(true, predicate_fn.eval(Path::new("src")));
98+
/// assert_eq!(false, predicate_fn.eval(Path::new("non-existent-file.foo")));
99+
/// ```
100+
pub fn is_dir() -> FileTypePredicate {
101+
FileTypePredicate {
102+
ft: FileType::Dir,
103+
follow: false,
104+
}
105+
}
106+
107+
/// Creates a new `Predicate` that ensures the path points to a symlink.
108+
///
109+
/// # Examples
110+
///
111+
/// ```
112+
/// use std::path::Path;
113+
/// use predicates::predicate::*;
114+
///
115+
/// let predicate_fn = path::is_symlink();
116+
/// assert_eq!(false, predicate_fn.eval(Path::new("Cargo.toml")));
117+
/// assert_eq!(false, predicate_fn.eval(Path::new("src")));
118+
/// assert_eq!(false, predicate_fn.eval(Path::new("non-existent-file.foo")));
119+
/// ```
120+
pub fn is_symlink() -> FileTypePredicate {
121+
FileTypePredicate {
122+
ft: FileType::Symlink,
123+
follow: false,
124+
}
125+
}

src/predicate/path/mod.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// Copyright (c) 2018 The predicates-rs Project Developers.
2+
//
3+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
4+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
5+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
6+
// option. This file may not be copied, modified, or distributed
7+
// except according to those terms.
8+
9+
//! Path Predicates
10+
//!
11+
//! This module contains predicates specific to the file system.
12+
13+
mod existence;
14+
pub use self::existence::{exists, missing, ExistencePredicate};
15+
mod ft;
16+
pub use self::ft::{is_dir, is_file, is_symlink, FileTypePredicate};

0 commit comments

Comments
 (0)