use pretty_assertions::assert_eq as pretty_assert_eq; use std::path::Path; use std::process::Command; /// Assert that all files at `expected` path exist at `actual` path and the contents of the files /// is the same. pub fn assert_matches_expected( base: &Path, actual: &Path, expected: &Path, ) -> Result<(), anyhow::Error> { assert_eq!( actual.is_file(), expected.is_file(), "actual '{}' is_file({}) != expected '{}' is_file({})", actual.display(), actual.is_file(), expected.display(), expected.is_file() ); if actual.is_file() { let actual_content = std::fs::read_to_string(actual)?; let expected_content = std::fs::read_to_string(expected)?; pretty_assert_eq!( PrettyString(&actual_content), PrettyString(&expected_content), "contents of actual file '{}' differs from the contents of expected file '{}'", actual.strip_prefix(base)?.display(), expected.strip_prefix(base)?.display() ); } else { let mut expected_it = std::fs::read_dir(expected)?; while let Some(expected_child) = expected_it.next() { let expected_child = expected_child?; let name = expected_child.file_name(); let actual_child = actual.join(&name); assert!( actual_child.exists(), "expected file '{}' does not exists in actual output '{}'", Path::new(&name).display(), actual.strip_prefix(base)?.display() ); assert_matches_expected(base, &actual_child, &expected_child.path())?; } // Make sure everything in `actual` also exists in `expected` let mut actual_it = std::fs::read_dir(actual)?; while let Some(actual_child) = actual_it.next() { let actual_child = actual_child?; let name = actual_child.file_name(); let expected_child = expected.join(&name); // We ignore Cargo.lock if name != "Cargo.lock" { assert!( expected_child.exists(), "actual file '{}' does not exists in expected output '{}'", Path::new(&name).display(), actual.strip_prefix(base)?.display() ); } } } Ok(()) } /// Wrapper around string slice that makes debug output `{:?}` to print string same way as `{}`. /// Used in different `assert*!` macros in combination with `pretty_assertions` crate to make /// test failures to show nice diffs. #[derive(PartialEq, Eq)] #[doc(hidden)] pub struct PrettyString<'a>(pub &'a str); /// Make diff to display string as multi-line string impl<'a> std::fmt::Debug for PrettyString<'a> { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { f.write_str(self.0) } } /// Install `rustfmt` component so our generator can use it. pub fn install_rustfmt(path: &Path) -> Result<(), anyhow::Error> { let output = Command::new("rustup") .arg("component") .arg("add") .arg("rustfmt") .current_dir(path.canonicalize()?) .output()?; // Ignore status, but print to the console if !output.status.success() { let err = String::from_utf8(output.stderr)?; eprintln!( "Warning: failed to install rust fmt (exit code {}): {}", output.status.code().unwrap_or(0), err ); } Ok(()) }