snapbox/macros.rs
1/// Check if a value is the same as an expected value
2///
3/// By default [`filters`][crate::filter] are applied, including:
4/// - `...` is a line-wildcard when on a line by itself
5/// - `[..]` is a character-wildcard when inside a line
6/// - `[EXE]` matches `.exe` on Windows
7/// - `"{...}"` is a JSON value wildcard
8/// - `"...": "{...}"` is a JSON key-value wildcard
9/// - `\` to `/`
10/// - Newlines
11///
12/// To limit this to newline normalization for text, call [`Data::raw`][crate::Data] on `expected`.
13///
14/// # Effective signature
15///
16/// ```rust
17/// # use snapbox::IntoData;
18/// fn assert_data_eq(actual: impl IntoData, expected: impl IntoData) {
19/// // ...
20/// }
21/// ```
22///
23/// # Examples
24///
25/// ```rust
26/// # use snapbox::assert_data_eq;
27/// let output = "something";
28/// let expected = "so[..]g";
29/// assert_data_eq!(output, expected);
30/// ```
31///
32/// Can combine this with [`file!`]
33/// ```rust,no_run
34/// # use snapbox::assert_data_eq;
35/// # use snapbox::file;
36/// let actual = "something";
37/// assert_data_eq!(actual, file!["output.txt"]);
38/// ```
39#[macro_export]
40macro_rules! assert_data_eq {
41 ($actual: expr, $expected: expr $(,)?) => {{
42 let actual = $crate::IntoData::into_data($actual);
43 let expected = $crate::IntoData::into_data($expected);
44 $crate::Assert::new()
45 .action_env($crate::assert::DEFAULT_ACTION_ENV)
46 .eq(actual, expected);
47 }};
48}
49
50/// Find the directory for your source file
51///
52/// By default, a heuristic is used.
53///
54/// To override the heuristic, add to `$WORKSPACE_ROOT/.cargo/config.toml`:
55/// ```toml
56/// [env]
57/// CARGO_RUSTC_CURRENT_DIR = { value = "", relative = true }
58/// ```
59#[doc(hidden)] // forced to be visible in intended location
60#[macro_export]
61macro_rules! current_dir {
62 () => {{
63 let root = $crate::utils::cargo_rustc_current_dir!();
64 let file = ::std::file!();
65 let rel_path = ::std::path::Path::new(file).parent().unwrap();
66 root.join(rel_path)
67 }};
68}
69
70/// Find the directory for your source file
71///
72/// By default, a heuristic is used.
73///
74/// To override the heuristic, add to `$WORKSPACE_ROOT/.cargo/config.toml`:
75/// ```toml
76/// [env]
77/// CARGO_RUSTC_CURRENT_DIR = { value = "", relative = true }
78/// ```
79#[doc(hidden)] // forced to be visible in intended location
80#[macro_export]
81macro_rules! current_rs {
82 () => {{
83 let root = $crate::utils::cargo_rustc_current_dir!();
84 let file = ::std::file!();
85 let rel_path = ::std::path::Path::new(file);
86 root.join(rel_path)
87 }};
88}
89
90/// Find the base directory for [`std::file!`]
91///
92/// By default, a heuristic is used.
93///
94/// To override the heuristic, add to `$WORKSPACE_ROOT/.cargo/config.toml`:
95/// ```toml
96/// [env]
97/// CARGO_RUSTC_CURRENT_DIR = { value = "", relative = true }
98/// ```
99#[doc(hidden)] // forced to be visible in intended location
100#[macro_export]
101macro_rules! cargo_rustc_current_dir {
102 () => {{
103 if let Some(rustc_root) = ::std::option_env!("CARGO_RUSTC_CURRENT_DIR") {
104 ::std::path::Path::new(rustc_root)
105 } else {
106 let manifest_dir = ::std::path::Path::new(::std::env!("CARGO_MANIFEST_DIR"));
107 manifest_dir
108 .ancestors()
109 .filter(|it| it.join("Cargo.toml").exists())
110 .last()
111 .unwrap()
112 }
113 }};
114}
115
116/// Path to the current function
117///
118/// Closures are ignored
119#[doc(hidden)]
120#[macro_export]
121macro_rules! fn_path {
122 () => {{
123 fn f() {}
124 fn type_name_of_val<T>(_: T) -> &'static str {
125 std::any::type_name::<T>()
126 }
127 let mut name = type_name_of_val(f).strip_suffix("::f").unwrap_or("");
128 while let Some(rest) = name.strip_suffix("::{{closure}}") {
129 name = rest;
130 }
131 name
132 }};
133}
134
135/// The absolute path to a binary target's executable.
136///
137/// The `bin_target_name` is the name of the binary
138/// target, exactly as-is.
139///
140/// **NOTE:** This is only set when building an integration test or benchmark.
141///
142/// ## Example
143///
144/// ```rust,no_run
145/// #[test]
146/// fn cli_tests() {
147/// trycmd::TestCases::new()
148/// .default_bin_path(trycmd::cargo_bin!("bin-fixture"))
149/// .case("tests/cmd/*.trycmd");
150/// }
151/// ```
152#[macro_export]
153#[doc(hidden)]
154macro_rules! cargo_bin {
155 () => {
156 $crate::cmd::cargo_bin!(env!("CARGO_PKG_NAME"))
157 };
158 ($bin_target_name:expr) => {
159 ::std::path::Path::new(env!(concat!("CARGO_BIN_EXE_", $bin_target_name)))
160 };
161}
162
163#[cfg(test)]
164mod test {
165 #[test]
166 fn direct_fn_path() {
167 assert_eq!(fn_path!(), "snapbox::macros::test::direct_fn_path");
168 }
169
170 #[test]
171 #[allow(clippy::redundant_closure_call)]
172 fn closure_fn_path() {
173 (|| {
174 assert_eq!(fn_path!(), "snapbox::macros::test::closure_fn_path");
175 })();
176 }
177
178 #[test]
179 fn nested_fn_path() {
180 fn nested() {
181 assert_eq!(fn_path!(), "snapbox::macros::test::nested_fn_path::nested");
182 }
183 nested();
184 }
185}