|
| 1 | +use std::fmt; |
| 2 | +use std::process; |
| 3 | +use std::rc; |
| 4 | + |
| 5 | +use difference::Changeset; |
| 6 | + |
| 7 | +use diff; |
1 | 8 | use self::errors::*;
|
2 | 9 | pub use self::errors::{Error, ErrorKind};
|
3 |
| -use diff; |
4 |
| -use difference::Changeset; |
5 |
| -use std::process; |
6 | 10 |
|
7 | 11 |
|
8 | 12 | #[derive(Debug, Clone, PartialEq, Eq)]
|
@@ -57,17 +61,42 @@ impl ContainsPredicate {
|
57 | 61 | }
|
58 | 62 | }
|
59 | 63 |
|
| 64 | +#[derive(Clone)] |
| 65 | +struct FnPredicate { |
| 66 | + pub pred: rc::Rc<Fn(&str) -> bool>, |
| 67 | + pub msg: String, |
| 68 | +} |
| 69 | + |
| 70 | +impl FnPredicate { |
| 71 | + pub fn verify_str(&self, got: &str) -> Result<()> { |
| 72 | + let pred = &self.pred; |
| 73 | + if ! pred(got) { |
| 74 | + bail!(ErrorKind::PredicateFailed(got.into(), self.msg.clone())); |
| 75 | + } |
| 76 | + |
| 77 | + Ok(()) |
| 78 | + } |
| 79 | +} |
| 80 | + |
| 81 | +impl fmt::Debug for FnPredicate { |
| 82 | + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
| 83 | + write!(f, "{}", self.msg) |
| 84 | + } |
| 85 | +} |
| 86 | + |
60 | 87 | #[derive(Debug, Clone)]
|
61 | 88 | enum StrPredicate {
|
62 | 89 | Is(IsPredicate),
|
63 | 90 | Contains(ContainsPredicate),
|
| 91 | + Fn(FnPredicate), |
64 | 92 | }
|
65 | 93 |
|
66 | 94 | impl StrPredicate {
|
67 | 95 | pub fn verify_str(&self, got: &str) -> Result<()> {
|
68 | 96 | match *self {
|
69 | 97 | StrPredicate::Is(ref pred) => pred.verify_str(got),
|
70 | 98 | StrPredicate::Contains(ref pred) => pred.verify_str(got),
|
| 99 | + StrPredicate::Fn(ref pred) => pred.verify_str(got), |
71 | 100 | }
|
72 | 101 | }
|
73 | 102 | }
|
@@ -159,6 +188,28 @@ impl Output {
|
159 | 188 | Self::new(StrPredicate::Is(pred))
|
160 | 189 | }
|
161 | 190 |
|
| 191 | + /// Expect the command output to satisfy the given predicate. |
| 192 | + /// |
| 193 | + /// # Examples |
| 194 | + /// |
| 195 | + /// ```rust |
| 196 | + /// extern crate assert_cli; |
| 197 | + /// |
| 198 | + /// assert_cli::Assert::command(&["echo", "-n", "42"]) |
| 199 | + /// .stdout().satisfies(|x| x.len() == 2, "bad length") |
| 200 | + /// .unwrap(); |
| 201 | + /// ``` |
| 202 | + pub fn satisfies<F, M>(pred: F, msg: M) -> Self |
| 203 | + where F: 'static + Fn(&str) -> bool, |
| 204 | + M: Into<String> |
| 205 | + { |
| 206 | + let pred = FnPredicate { |
| 207 | + pred: rc::Rc::new(pred), |
| 208 | + msg: msg.into(), |
| 209 | + }; |
| 210 | + Self::new(StrPredicate::Fn(pred)) |
| 211 | + } |
| 212 | + |
162 | 213 | fn new(pred: StrPredicate) -> Self {
|
163 | 214 | Self { pred }
|
164 | 215 | }
|
@@ -232,6 +283,10 @@ mod errors {
|
232 | 283 | description("Output was not as expected")
|
233 | 284 | display("expected to not match\noutput=```{}```", got)
|
234 | 285 | }
|
| 286 | + PredicateFailed(got: String, msg: String) { |
| 287 | + description("Output predicate failed") |
| 288 | + display("{}\noutput=```{}```", msg, got) |
| 289 | + } |
235 | 290 | OutputMismatch(kind: super::OutputKind) {
|
236 | 291 | description("Output was not as expected")
|
237 | 292 | display(
|
|
0 commit comments