Skip to content
This repository was archived by the owner on Dec 29, 2021. It is now read-only.

Commit f45db85

Browse files
committed
refactor assert_cmd! macro
- fix #22 - fix #15
1 parent 1f29f4f commit f45db85

File tree

4 files changed

+107
-41
lines changed

4 files changed

+107
-41
lines changed

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ build = "build.rs"
1616
colored = "1.4"
1717
difference = "1.0"
1818
error-chain = "0.10.0"
19+
rustc-serialize = "0.3"
1920

2021
[build-dependencies]
2122
skeptic = "0.5"

README.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -42,17 +42,17 @@ And here is one that will fail (which also shows `execute` which returns a
4242
#[macro_use] extern crate assert_cli;
4343

4444
fn main() {
45-
let test = assert_cmd!(grep amet Cargo.toml)
45+
let test = assert_cmd!(grep amet "Cargo.toml")
4646
.fails_with(1)
4747
.execute();
48-
assert!(test.is_err());
48+
assert!(test.is_ok());
4949
}
5050
```
5151

5252
If you want to match the program's output _exactly_, you can use
5353
`prints_exactly`:
5454

55-
```rust,should_panic="Assert CLI failure"
55+
```rust,should_panic
5656
#[macro_use] extern crate assert_cli;
5757
5858
fn main() {

src/lib.rs

Lines changed: 4 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@
7575

7676
extern crate difference;
7777
#[macro_use] extern crate error_chain;
78+
extern crate rustc_serialize;
7879

7980
use std::process::{Command, Output};
8081
use std::fmt;
@@ -84,6 +85,9 @@ use difference::Changeset;
8485
mod errors;
8586
use errors::*;
8687

88+
#[macro_use] mod macros;
89+
pub use macros::flatten_escaped_string;
90+
8791
mod diff;
8892

8993
/// Assertions for a specific command.
@@ -448,41 +452,3 @@ impl Assert {
448452
}
449453
}
450454
}
451-
452-
/// Easily construct an `Assert` with a custom command.
453-
///
454-
/// Make sure to include the crate as `#[macro_use] extern crate assert_cli;` if
455-
/// you want to use this macro.
456-
///
457-
/// # Examples
458-
///
459-
/// To test that our very complex cli applications succeeds and prints some
460-
/// text to stdout that contains
461-
///
462-
/// ```plain
463-
/// No errors whatsoever
464-
/// ```
465-
///
466-
/// ..., you would call it like this:
467-
///
468-
/// ```rust
469-
/// #[macro_use] extern crate assert_cli;
470-
/// # fn main() {
471-
/// assert_cmd!(echo "Launch sequence initiated.\nNo errors whatsoever!\n")
472-
/// .succeeds()
473-
/// .prints("No errors whatsoever")
474-
/// .unwrap();
475-
/// # }
476-
/// ```
477-
///
478-
/// The macro will try to convert its arguments as strings, but is limited by
479-
/// Rust's default tokenizer, e.g., you always need to quote CLI arguments
480-
/// like `"--verbose"`.
481-
#[macro_export]
482-
macro_rules! assert_cmd {
483-
($($x:tt)+) => {{
484-
$crate::Assert::command(
485-
&[$(stringify!($x)),*]
486-
)
487-
}}
488-
}

src/macros.rs

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
use std::borrow::Cow;
2+
use rustc_serialize::json::Json;
3+
4+
/// Easily construct an `Assert` with a custom command.
5+
///
6+
/// Make sure to include the crate as `#[macro_use] extern crate assert_cli;` if
7+
/// you want to use this macro.
8+
///
9+
/// # Examples
10+
///
11+
/// To test that our very complex cli applications succeeds and prints some
12+
/// text to stdout that contains
13+
///
14+
/// ```plain
15+
/// No errors whatsoever
16+
/// ```
17+
///
18+
/// ..., you would call it like this:
19+
///
20+
/// ```rust
21+
/// #[macro_use] extern crate assert_cli;
22+
/// # fn main() {
23+
/// assert_cmd!(echo "Launch sequence initiated.\nNo errors whatsoever!\n")
24+
/// .succeeds()
25+
/// .prints("No errors whatsoever")
26+
/// .unwrap();
27+
/// # }
28+
/// ```
29+
///
30+
/// The macro will try to convert its arguments as strings, but is limited by
31+
/// Rust's default tokenizer, e.g., you always need to quote CLI arguments
32+
/// like `"--verbose"`.
33+
#[macro_export]
34+
macro_rules! assert_cmd {
35+
($($x:tt)+) => {{
36+
$(__assert_single_token_expression!(@CHECK $x);)*
37+
38+
$crate::Assert::command(
39+
&[$(
40+
$crate::flatten_escaped_string(stringify!($x)).as_ref()
41+
),*]
42+
)
43+
}}
44+
}
45+
46+
/// Deserialize a JSON-encoded `String`.
47+
///
48+
/// # Panics
49+
///
50+
/// If `x` can not be decoded as `String`.
51+
#[doc(hidden)]
52+
fn deserialize_json_string(x: &str) -> String {
53+
match Json::from_str(x).expect(&format!("Unable to deserialize `{:?}` as string.", x)) {
54+
Json::String(deserialized) => deserialized,
55+
_ => panic!("Unable to deserialize `{:?}` as string.", x),
56+
}
57+
}
58+
59+
/// Deserialize a JSON-encoded `String`.
60+
///
61+
/// # Panics
62+
///
63+
/// If `x` can not be decoded as `String`.
64+
#[doc(hidden)]
65+
pub fn flatten_escaped_string(x: &str) -> Cow<str> {
66+
if x.starts_with('"') && x.ends_with('"') {
67+
Cow::Owned(deserialize_json_string(x))
68+
} else {
69+
Cow::Borrowed(x)
70+
}
71+
}
72+
73+
/// Inspect a single token and decide if it is safe to `stringify!`, without loosing
74+
/// information about whitespaces, to address https://github.com/killercup/assert_cli/issues/22.
75+
///
76+
/// Call like `__assert_single_token_expression!(@CHECK x)`, where `x` can be any token to check.
77+
///
78+
/// This macro will only accept single tokens, which parse as expressions, e.g.
79+
/// - strings "foo", r#"foo"
80+
/// - idents `foo`, `foo42`
81+
/// - numbers `42`
82+
/// - chars `'a'`
83+
///
84+
/// Delimited token trees `{...}` and the like are rejected. Everything thats not an expression
85+
/// will also be rejected.
86+
#[doc(hidden)]
87+
#[macro_export]
88+
macro_rules! __assert_single_token_expression {
89+
// deny `{...}`
90+
(@CHECK {$( $x:tt )*}) => { assert_cmd!(@DENY {$( $x )*}) };
91+
// deny `(...)`
92+
(@CHECK ($( $x:tt )*)) => { assert_cmd!(@DENY {$( $x )*}) };
93+
// deny `[...]`
94+
(@CHECK [$( $x:tt )*]) => { assert_cmd!(@DENY {$( $x )*}) };
95+
// only allow tokens that parse as expression
96+
(@CHECK $x:expr) => { };
97+
// little helper
98+
(@DENY) => { };
99+
}

0 commit comments

Comments
 (0)