use assert_cmd::Command;
use camino::{Utf8Path, Utf8PathBuf};
use cap_std::{ambient_authority, fs_utf8::Dir};
use rstest::rstest;
use tempfile::{TempDir, tempdir};
const TABLE: [&str; 3] = ["| A | B |", "| --- | --- |", "| 1 | 2 |"];
fn document(lines: &[&str], ending: &str, bom: bool) -> Vec<u8> {
let mut bytes = Vec::new();
if bom {
bytes.extend_from_slice("\u{FEFF}".as_bytes());
}
for line in lines {
bytes.extend_from_slice(line.as_bytes());
bytes.extend_from_slice(ending.as_bytes());
}
bytes
}
fn escaped(bytes: &[u8]) -> String { String::from_utf8_lossy(bytes).escape_debug().to_string() }
const FIXTURE_NAME: &str = "fixture.dat";
fn capability_directory(tempdir: &TempDir) -> (Dir, Utf8PathBuf) {
let path = Utf8PathBuf::from_path_buf(tempdir.path().to_path_buf())
.expect("temporary directory path is UTF-8");
let directory = Dir::open_ambient_dir(&path, ambient_authority())
.expect("failed to open temporary directory");
(directory, path)
}
fn rewrite_in_place(input: &[u8]) -> Vec<u8> {
let dir = tempdir().expect("temporary directory");
let (directory, root) = capability_directory(&dir);
let name = Utf8Path::new(FIXTURE_NAME);
directory.write(name, input).expect("write fixture");
Command::cargo_bin("mdtablefix")
.expect("cargo binary")
.arg("--in-place")
.arg(root.join(FIXTURE_NAME).as_std_path())
.assert()
.success();
directory.read(name).expect("read result")
}
#[rstest]
#[case::crlf_ragged(include_bytes!("data/document/crlf_ragged.dat"), document(&TABLE, "\r\n", false))]
#[case::crlf_clean(include_bytes!("data/document/crlf_clean.dat"), document(&TABLE, "\r\n", false))]
#[case::mixed_lf_majority(
include_bytes!("data/document/mixed_lf_majority.dat"),
document(&TABLE, "\n", false)
)]
#[case::mixed_crlf_majority(
include_bytes!("data/document/mixed_crlf_majority.dat"),
document(&TABLE, "\r\n", false)
)]
#[case::mixed_tie(include_bytes!("data/document/mixed_tie.dat"), document(&["alpha", "beta"], "\n", false))]
#[case::mixed_in_fence(
include_bytes!("data/document/mixed_in_fence.dat"),
document(&["| A | B |", "| --- | --- |", "| 1 | 2 |", "", "```sh", "echo hi", "```"], "\r\n", false)
)]
#[case::bom_ragged(
include_bytes!("data/document/bom_ragged.dat"),
document(&TABLE, "\n", true)
)]
#[case::bom_crlf_clean(
include_bytes!("data/document/bom_crlf_clean.dat"),
document(&TABLE, "\r\n", true)
)]
#[case::lone_cr(include_bytes!("data/document/lone_cr.dat"), b"alpha\rbeta\n".to_vec())]
#[case::empty(include_bytes!("data/document/empty.dat"), Vec::new())]
#[case::no_trailing_newline(
include_bytes!("data/document/no_trailing_newline.dat"),
document(&TABLE, "\n", false)
)]
fn in_place_document_boundary(#[case] input: &[u8], #[case] expected: Vec<u8>) {
let actual = rewrite_in_place(input);
assert_eq!(escaped(&actual), escaped(&expected));
}
#[test]
fn stdin_preserves_line_endings() {
let stdout = Command::cargo_bin("mdtablefix")
.expect("cargo binary")
.write_stdin(b"|A|B|\r\n|---|---|\r\n|1|2|\r\n".to_vec())
.assert()
.success()
.get_output()
.stdout
.clone();
assert_eq!(
escaped(&stdout),
escaped(&document(&TABLE, "\r\n", false)),
"stdin should be reflowed and keep its line-ending style"
);
}