diff --git a/scripts/ci.sh b/scripts/ci.sh index e071230..5d43793 100755 --- a/scripts/ci.sh +++ b/scripts/ci.sh @@ -9,6 +9,7 @@ echo "Starting build at: ${start} on ${host_name}" export RUST_BACKTRACE="full" cargo deny check +cargo +nightly fmt --all -- --check cargo build --verbose cargo test --verbose --all-features cargo clippy --workspace --all-targets --all-features -- --deny warnings diff --git a/src/macros_utils.rs b/src/macros_utils.rs index a9278f7..3c7fcb5 100644 --- a/src/macros_utils.rs +++ b/src/macros_utils.rs @@ -1,4 +1,5 @@ use std::collections::BTreeMap; +use std::io::IsTerminal as _; use std::ops::Range; use codespan_reporting::diagnostic::{Diagnostic, Label}; @@ -89,13 +90,20 @@ pub fn format_error<'a>(json: &'a Value, error: &Error<'a>) -> String { ) .with_message(error.to_string())]); - let output = Vec::::new(); - let mut writer = termcolor::Ansi::new(output); let config = term::Config::default(); + let bytes = Vec::::new(); + + let bytes = if std::io::stderr().is_terminal() { + let mut writer = termcolor::Ansi::new(bytes); + term::emit(&mut writer, &config, &files, &diagnostic).unwrap(); + writer.into_inner() + } else { + let mut writer = termcolor::NoColor::new(bytes); + term::emit(&mut writer, &config, &files, &diagnostic).unwrap(); + writer.into_inner() + }; - term::emit(&mut writer, &config, &files, &diagnostic).unwrap(); - - String::from_utf8(writer.into_inner()).unwrap() + String::from_utf8(bytes).unwrap() } /// Serialize a JSON [Value] and keeps the span information of each diff --git a/tests/error_msg.rs b/tests/error_msg.rs index 98ad14b..48e1480 100644 --- a/tests/error_msg.rs +++ b/tests/error_msg.rs @@ -1,17 +1,34 @@ +use std::any::Any; +use std::io::IsTerminal as _; + use assert_json::{assert_json, validators}; use indoc::indoc; macro_rules! assert_panic_output { - ($output:expr, $($assert:tt)+) => {{ + ($expected_output:expr, $($assert:tt)+) => {{ let out_result = std::panic::catch_unwind(|| $($assert)+); - let out_err = out_result.err().unwrap(); - assert!(out_err.is::()); - let out = out_err.downcast_ref::().unwrap(); - let out = String::from_utf8(strip_ansi_escapes::strip(out.clone().into_bytes())).unwrap(); - assert!(out.contains($output.trim()), "\n\texpected:\n{}\n\tgot:\n{}", $output.trim(), out) + let err = out_result_to_string(out_result); + let expected_output = $expected_output.trim(); + assert!(err.contains(expected_output), "\n\texpected:\n{expected_output}\n\tgot:\n{err}") }}; } +#[expect(unsafe_code)] +fn out_result_to_string(result: Result<(), Box>) -> String { + let err = result.unwrap_err(); + let s = err + .downcast::() + .expect("the assert output should be a String"); + + // ANSI escapes should only be written when `assert_json!` is called from a terminal + if std::io::stderr().is_terminal() { + let bytes = strip_ansi_escapes::strip(s.into_bytes()); + unsafe { String::from_utf8_unchecked(bytes) } + } else { + *s + } +} + #[test] fn primitive_invalid_type() { let expected_output = indoc! {r"