use std::fmt::Write as _;
#[must_use]
pub fn canonical(text: &str) -> String {
if text.contains("\r\n") {
let lf = text.replace("\r\n", "\n");
return canonical(&lf).replace('\n', "\r\n");
}
let (front, body) = crate::adr::split_frontmatter_verbatim(text);
let has_front = body.len() != text.len();
let is_adr = crate::adr::declares_adr(text);
let mut out = String::with_capacity(text.len());
if has_front {
out.push_str("---\n");
out.push_str(&canonical_frontmatter(front));
out.push_str(if body.is_empty() && !text.ends_with('\n') {
"---"
} else {
"---\n"
});
}
out.push_str(&canonical_body(body, is_adr));
out
}
fn canonical_frontmatter(front: &str) -> String {
let mut out = String::with_capacity(front.len());
for line in front.lines() {
let _ = writeln!(out, "{}", normalise_value(line));
}
out
}
fn normalise_value(line: &str) -> String {
let Some((key, rest)) = line.split_once(':') else {
return line.to_owned();
};
if !key.trim().eq_ignore_ascii_case("last-modified") || key.starts_with(char::is_whitespace) {
return line.to_owned();
}
let (value, comment) = match rest.find(" #") {
Some(i) => (&rest[..i], rest[i..].trim_start()),
None => (rest, ""),
};
let bare = crate::adr::clean_value(value);
let quote = match value.trim().chars().next() {
Some(q @ ('"' | '\'')) => q.to_string(),
_ => String::new(),
};
let parts: Vec<&str> = bare.split('-').collect();
let [y, m, d] = parts.as_slice() else {
return line.to_owned();
};
let ok = y.len() == 4 && (1..=2).contains(&m.len()) && (1..=2).contains(&d.len());
let (Ok(y), Ok(m), Ok(d)) = (y.parse::<u32>(), m.parse::<u32>(), d.parse::<u32>()) else {
return line.to_owned();
};
if !ok || !(1..=12).contains(&m) || !(1..=31).contains(&d) {
return line.to_owned();
}
let tail = if comment.is_empty() {
String::new()
} else {
format!(" {comment}")
};
let lead = &rest[..rest.len() - rest.trim_start().len()];
let lead = if lead.is_empty() { " " } else { lead };
format!("{key}:{lead}{quote}{y:04}-{m:02}-{d:02}{quote}{tail}")
}
fn canonical_body(body: &str, is_adr: bool) -> String {
let mut out = String::with_capacity(body.len());
let mut fence: Option<(char, usize)> = None;
let mut seen_table = false;
let lines: Vec<&str> = body.lines().collect();
let mut i = 0;
while i < lines.len() {
let line = lines[i];
if let Some((ch, len)) = fence {
let _ = writeln!(out, "{line}");
if fence_of(line).is_some_and(|(c, n)| {
c == ch && n >= len && line.trim_start().trim_start_matches(c).trim().is_empty()
}) {
fence = None;
}
i += 1;
continue;
}
if let Some(f) = fence_of(line) {
fence = Some(f);
let _ = writeln!(out, "{line}");
i += 1;
continue;
}
if is_table_line(line) {
let mut j = i;
while j < lines.len() && is_table_line(lines[j]) {
j += 1;
}
let block = &lines[i..j];
let is_table = block.len() > 1 && is_separator_row(block[1]);
if is_table {
let summary = !seen_table;
seen_table = true;
let mut relabelled = false;
for l in block {
let indent = &l[..l.len() - l.trim_start().len()];
let row = format!("{indent}{}", canonical_table_row(l.trim()));
let row = if is_adr && summary && !relabelled && is_state_row(&row) {
relabelled = true;
relabel_state(&row)
} else {
row
};
let _ = writeln!(out, "{row}");
}
} else {
for l in block {
let _ = writeln!(out, "{l}");
}
}
i = j;
continue;
}
let _ = writeln!(out, "{line}");
i += 1;
}
if body.ends_with('\n') || body.is_empty() {
out
} else {
out.trim_end_matches('\n').to_owned()
}
}
fn fence_of(line: &str) -> Option<(char, usize)> {
if !markdown_indented(line) {
return None;
}
let t = line.trim_start();
for ch in ['`', '~'] {
let n = t.chars().take_while(|c| *c == ch).count();
if n < 3 {
continue;
}
if ch == '`' && t[n..].contains('`') {
continue;
}
return Some((ch, n));
}
None
}
fn is_table_line(line: &str) -> bool {
let t = line.trim_start();
markdown_indented(line) && t.starts_with('|') && t.trim_end().len() > 1 && t[1..].contains('|')
}
fn markdown_indented(line: &str) -> bool {
let indent = &line[..line.len() - line.trim_start().len()];
!indent.contains('\t') && indent.len() <= 3
}
fn is_separator_row(line: &str) -> bool {
let cells = split_cells(line.trim());
!cells.is_empty()
&& cells.iter().all(|c| {
let core = c.strip_prefix(':').unwrap_or(c);
let core = core.strip_suffix(':').unwrap_or(core);
core.len() >= 3 && core.chars().all(|ch| ch == '-')
})
}
fn relabel_state(row: &str) -> String {
if row.trim_start().starts_with("| **Status** |") {
row.replacen("| **Status** |", "| **State** |", 1)
} else {
row.to_owned()
}
}
fn is_state_row(row: &str) -> bool {
let row = row.trim_start();
row.starts_with("| **State** |") || row.starts_with("| **Status** |")
}
fn canonical_table_row(row: &str) -> String {
let cells = split_cells(row);
let separator = is_separator_row(row);
let mut out = String::with_capacity(row.len());
out.push('|');
for cell in &cells {
if separator {
let left = cell.starts_with(':');
let right = cell.ends_with(':');
let bar = match (left, right) {
(true, true) => ":---:",
(true, false) => ":---",
(false, true) => "---:",
(false, false) => "---",
};
let _ = write!(out, "{bar}|");
} else {
let _ = write!(out, " {cell} |");
}
}
out
}
fn split_cells(row: &str) -> Vec<String> {
let trimmed = row.trim();
let body = trimmed.strip_prefix('|').unwrap_or(trimmed);
let inner = match body.strip_suffix('|') {
Some(rest) if !ends_escaped(rest) => rest,
_ => body,
};
let spans = crate::text::code_spans(inner);
let in_code = |at: usize| spans.iter().any(|(s, e)| at >= *s && at < *e);
let mut cells = Vec::new();
let mut cur = String::new();
let mut escaped = false;
for (at, ch) in inner.char_indices() {
if escaped {
cur.push(ch);
escaped = false;
} else if ch == '\\' {
cur.push(ch);
escaped = true;
} else if ch == '|' && !in_code(at) {
cells.push(cur.trim().to_owned());
cur = String::new();
} else {
cur.push(ch);
}
}
cells.push(cur.trim().to_owned());
cells
}
fn ends_escaped(s: &str) -> bool {
s.chars().rev().take_while(|c| *c == '\\').count() % 2 == 1
}
fn diff_lines(text: &str) -> Vec<&str> {
text.split_inclusive('\n')
.map(|l| l.strip_suffix('\n').unwrap_or(l))
.collect()
}
#[must_use]
pub fn unified_diff(path: &str, before: &str, after: &str) -> Option<String> {
const CTX: usize = 3;
if before == after {
return None;
}
let (a, b): (Vec<&str>, Vec<&str>) = (diff_lines(before), diff_lines(after));
if a == b {
let last = |s: &str| s.lines().next_back().unwrap_or_default().to_owned();
let n = before.lines().count().max(1);
let mut out = format!("--- {path}\n+++ {path}\n@@ -{n},1 +{n},1 @@\n");
let _ = write!(out, "-{}", last(before));
if !before.ends_with('\n') {
let _ = write!(out, "\n\\ No newline at end of file");
}
let _ = write!(out, "\n+{}", last(after));
if !after.ends_with('\n') {
let _ = write!(out, "\n\\ No newline at end of file");
}
out.push('\n');
return Some(out);
}
let mut lcs = vec![vec![0_usize; b.len() + 1]; a.len() + 1];
for i in (0..a.len()).rev() {
for j in (0..b.len()).rev() {
lcs[i][j] = if a[i] == b[j] {
lcs[i + 1][j + 1] + 1
} else {
lcs[i + 1][j].max(lcs[i][j + 1])
};
}
}
let mut ops: Vec<(char, usize, usize)> = Vec::new();
let (mut i, mut j) = (0, 0);
while i < a.len() && j < b.len() {
if a[i] == b[j] {
ops.push((' ', i, j));
i += 1;
j += 1;
} else if lcs[i + 1][j] >= lcs[i][j + 1] {
ops.push(('-', i, j));
i += 1;
} else {
ops.push(('+', i, j));
j += 1;
}
}
while i < a.len() {
ops.push(('-', i, j));
i += 1;
}
while j < b.len() {
ops.push(('+', i, j));
j += 1;
}
let changed: Vec<usize> = ops
.iter()
.enumerate()
.filter(|(_, (t, _, _))| *t != ' ')
.map(|(n, _)| n)
.collect();
let mut out = format!("--- {path}\n+++ {path}\n");
let mut at = 0;
while at < changed.len() {
let start = changed[at].saturating_sub(CTX);
let mut end = changed[at];
while at + 1 < changed.len() && changed[at + 1] <= end + 2 * CTX {
at += 1;
end = changed[at];
}
at += 1;
let end = (end + CTX).min(ops.len() - 1);
let (mut old_n, mut new_n) = (0, 0);
for (t, _, _) in &ops[start..=end] {
if *t != '+' {
old_n += 1;
}
if *t != '-' {
new_n += 1;
}
}
let _ = writeln!(
out,
"@@ -{},{} +{},{} @@",
if old_n == 0 { 0 } else { ops[start].1 + 1 },
old_n,
if new_n == 0 { 0 } else { ops[start].2 + 1 },
new_n
);
for (t, oi, ni) in &ops[start..=end] {
let text = if *t == '+' { b[*ni] } else { a[*oi] };
let _ = writeln!(out, "{t}{text}");
let last_old = *t != '+' && *oi + 1 == a.len() && !before.ends_with('\n');
let last_new = *t != '-' && *ni + 1 == b.len() && !after.ends_with('\n');
if last_old || last_new {
let _ = writeln!(out, "\\ No newline at end of file");
}
}
}
Some(out)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn a_table_row_is_single_spaced() {
assert_eq!(canonical_table_row("|a|b|"), "| a | b |");
assert_eq!(canonical_table_row("| a | b |"), "| a | b |");
assert_eq!(canonical_table_row("| | |"), "| | |");
}
#[test]
fn a_separator_row_keeps_its_alignment_colons() {
assert_eq!(canonical_table_row("|---|---|"), "|---|---|");
assert_eq!(canonical_table_row("| :--- | ---: |"), "|:---|---:|");
assert_eq!(canonical_table_row("|:-------:|"), "|:---:|");
}
#[test]
fn an_escaped_pipe_does_not_add_a_column() {
assert_eq!(split_cells(r"| a \| b | c |").len(), 2);
assert_eq!(canonical_table_row(r"|a \| b|c|"), r"| a \| b | c |");
}
#[test]
fn a_pipe_inside_inline_code_is_not_a_column_boundary() {
let row = "| a | matches `<|im_start|>` here | b |";
assert_eq!(split_cells(row).len(), 3, "{:?}", split_cells(row));
assert_eq!(
canonical_table_row(row),
row,
"the formatter rewrote its own input"
);
}
#[test]
fn a_table_inside_a_fence_is_left_alone() {
let src = "| a | b |\n|---|---|\n\n```md\n| x |y|\n```\n";
let got = canonical_body(src, true);
assert!(got.contains("| a | b |"), "{got}");
assert!(
got.contains("| x |y|"),
"the fenced example was rewritten:\n{got}"
);
}
#[test]
fn a_date_is_normalised_to_iso() {
assert_eq!(
normalise_value("last-modified: 2026-9-1"),
"last-modified: 2026-09-01"
);
assert_eq!(
normalise_value("last-modified: 2026-09-01 # set by hand"),
"last-modified: 2026-09-01 # set by hand"
);
assert_eq!(
normalise_value("last-modified: soon"),
"last-modified: soon"
);
assert_eq!(normalise_value("version: \"1.2\""), "version: \"1.2\"");
}
#[test]
fn the_summary_row_settles_on_the_generators_spelling() {
let got = canonical_body("| **Status** | Accepted |\n|---|---|\n", true);
assert_eq!(got, "| **State** | Accepted |\n|---|---|\n");
assert_eq!(
canonical_body("The **Status** of this.\n", true),
"The **Status** of this.\n"
);
}
#[test]
fn an_unchanged_document_has_no_diff() {
assert!(unified_diff("a.md", "x\ny\n", "x\ny\n").is_none());
let d = unified_diff("a.md", "x\ny\n", "x\nz\n").expect("changed");
assert!(d.contains("-y") && d.contains("+z"), "{d}");
}
#[test]
fn the_canonical_form_is_a_fixed_point() {
let src = "---\nversion: \"1.0\"\n# note\nstatus: Accepted\nTitle: T\nodd: 1\n---\n\n\
| **Status** | Accepted |\n| a |b |\n|---|:--:|\n\n```md\n| raw |\n```\n";
let once = canonical(src);
assert_eq!(canonical(&once), once, "not idempotent:\n{once}");
}
}
#[cfg(test)]
mod review_regressions {
use super::*;
#[test]
fn a_pipe_line_is_not_a_table_without_a_separator() {
assert_eq!(canonical_body("|\n", true), "|\n");
assert_eq!(canonical_body("| a |b|\n", true), "| a |b|\n");
assert_eq!(
canonical_body("| a |b|\n|---|---|\n", true),
"| a | b |\n|---|---|\n"
);
assert_eq!(canonical_body(" | a |b|\n", true), " | a |b|\n");
}
#[test]
fn two_hyphens_are_content_not_a_separator() {
assert!(!is_separator_row("| -- | -- |"));
assert!(is_separator_row("| --- | --- |"));
assert_eq!(
canonical_body("| a | b |\n|---|---|\n| -- | -- |\n", true),
"| a | b |\n|---|---|\n| -- | -- |\n"
);
}
#[test]
fn an_empty_leading_cell_is_not_eaten() {
assert_eq!(split_cells("|| value |"), vec!["", "value"]);
}
#[test]
fn a_multi_backtick_span_protects_its_pipe() {
let cells = split_cells("| ``a ` | b`` | c |");
assert_eq!(cells.len(), 2, "{cells:?}");
assert_eq!(cells[0], "``a ` | b``");
}
#[test]
fn a_longer_fence_survives_a_shorter_one_inside_it() {
let src = "````md\n```\n| x |y|\n```\n````\n| a |b|\n|---|---|\n";
let got = canonical_body(src, true);
assert!(got.contains("| x |y|"), "fenced example rewritten:\n{got}");
assert!(
got.contains("| a | b |"),
"real table not formatted:\n{got}"
);
}
#[test]
fn the_state_relabel_reaches_a_fixed_point_in_one_pass() {
let src = "|**Status**| Accepted |\n|---|---|\n";
let once = canonical_body(src, true);
assert_eq!(once, "| **State** | Accepted |\n|---|---|\n");
assert_eq!(canonical_body(&once, true), once);
}
#[test]
fn the_relabel_is_adr_only() {
let src = "| **Status** | Accepted |\n|---|---|\n";
assert_eq!(canonical_body(src, false), src);
}
#[test]
fn a_date_that_is_not_one_is_left_alone() {
assert_eq!(
normalise_value("last-modified: 12345-1-1"),
"last-modified: 12345-1-1"
);
assert_eq!(
normalise_value("last-modified: 2026-13-01"),
"last-modified: 2026-13-01"
);
assert_eq!(
normalise_value("last-modified: 2026-09-01#tag"),
"last-modified: 2026-09-01#tag"
);
}
#[test]
fn frontmatter_closed_at_end_of_file_is_found() {
let got = canonical("---\nversion: \"1.0\"\nTitle: T\n---");
assert!(got.starts_with("---\nversion: \"1.0\"\n"), "{got}");
}
#[test]
fn the_diff_is_a_real_unified_diff() {
let mut before = String::new();
for n in 1..=20 {
let _ = writeln!(before, "line {n}");
}
let after = before.replace("line 10\n", "changed\n");
let d = unified_diff("a.md", &before, &after).expect("changed");
assert!(d.starts_with("--- a.md\n+++ a.md\n@@ "), "{d}");
assert!(d.contains("-line 10\n+changed\n"), "{d}");
assert!(d.contains(" line 7\n"), "no leading context:\n{d}");
assert!(!d.contains("line 1\nline 2"), "whole file emitted:\n{d}");
}
}
#[cfg(test)]
mod second_round {
use super::*;
#[test]
fn the_declared_kind_decides_the_relabel() {
let row = "| **Status** | Accepted |\n|---|---|\n";
let doc = |ty: &str| format!("---\ntype: {ty}\n---\n\n{row}");
assert!(canonical(&doc("adr")).contains("**State**"));
assert!(
canonical(&doc("ADR")).contains("**State**"),
"case-sensitive"
);
assert!(
canonical(&doc("not-adr")).contains("**Status**"),
"`not-adr` was treated as an ADR"
);
assert!(canonical(&doc("blueprint")).contains("**Status**"));
}
#[test]
fn a_fence_is_not_closed_by_a_line_carrying_an_info_string() {
let src = "```md\n```not-a-close\n| x |y|\n|---|---|\n```\n";
let got = canonical_body(src, true);
assert!(
got.contains("| x |y|"),
"a row inside the still-open fence was reformatted:\n{got}"
);
}
#[test]
fn a_trailing_newline_change_is_reported_rather_than_shown_as_empty() {
let d = unified_diff("a.md", "x\ny\n", "x\ny").expect("changed");
assert!(d.contains("@@"), "a diff with no hunk:\n{d}");
assert!(d.contains("\\ No newline at end of file"), "{d}");
assert!(
!d.contains("file ended"),
"an invented marker survived:\n{d}"
);
}
}
#[cfg(test)]
mod third_round {
use super::*;
#[test]
fn only_the_first_table_is_the_summary_table() {
let doc = "---\ntype: adr\n---\n\n| **Status** | Accepted |\n|---|---|\n\n\
## Later\n\n| **Status** | what it means |\n|---|---|\n";
let got = canonical(doc);
assert!(got.contains("| **State** | Accepted |"), "{got}");
assert!(
got.contains("| **Status** | what it means |"),
"a later table's own Status column was rewritten:\n{got}"
);
}
#[test]
fn the_shared_declaration_rule_decides() {
let row = "| **Status** | Accepted |\n|---|---|\n";
for spelling in ["type: adr", "Type: adr", "type : adr", "type: ADR"] {
let doc = format!("---\nadr-id: \"0001\"\n{spelling}\n---\n\n{row}");
assert!(
canonical(&doc).contains("**State**"),
"`{spelling}` was not read as an ADR"
);
}
let doc = format!("---\ntype: blueprint\n---\n\n{row}");
assert!(
canonical(&doc).contains("**Status**"),
"a blueprint was relabelled"
);
}
#[test]
fn the_hunk_header_is_valid_at_the_empty_file_boundary() {
let d = unified_diff("a.md", "", "added\n").expect("changed");
assert!(
d.contains("@@ -0,0 +1,1 @@"),
"insertion into an empty file:\n{d}"
);
let d = unified_diff("a.md", "gone\n", "").expect("changed");
assert!(
d.contains("@@ -1,1 +0,0 @@"),
"deletion to an empty file:\n{d}"
);
}
}
#[cfg(test)]
mod properties {
use super::*;
use std::collections::BTreeMap;
const FRAGMENTS: &[&str] = &[
"# Heading\n",
"prose with a | pipe in it\n",
"|\n",
"| a | b |\n|---|---|\n| 1 | 2 |\n",
"| a |b |\n| --- | --- |\n",
"| -- | -- |\n",
"|| empty first |\n|---|---|\n",
"| a \\| b | c |\n|---|---|\n",
"| a \\|\n|---|\n",
"| `<|im_start|>` | x |\n|---|---|\n",
"| ``a ` | b`` | c |\n|---|---|\n",
"| a ` | b |\n|---|---|\n",
"| ::--- | ---:: |\n",
"| **Status** | Accepted |\n|---|---|\n| **Status** | what it means |\n",
"| **Status** | Accepted |\n|---|---|\n",
"```md\n| x |y|\n```\n",
"````md\n```\n| x |y|\n```\n````\n",
"```md\n```not-a-close\n| x |y|\n```\n",
" | indented |code|\n",
"\t| tab indented |code|\n",
" ```\n",
"\t```\n",
"last-modified : 2026-9-1\n",
"| :--- | ---: | :---: |\n",
"\n",
];
const FRONTS: &[&str] = &[
"",
"---\n---\n",
"---\n\n---\n",
"---\ntype: adr\nadr-id: \"0001\"\nversion: \"1.0\"\nTitle: T\n---\n",
"---\nType: adr\n# a comment\nlast-modified: 2026-9-1\n---\n",
"---\nsite-page: x/y\nstatus: deprecated\n---\n",
];
fn says(text: &str) -> BTreeMap<String, usize> {
let mut out = BTreeMap::new();
let mut fence: Option<(char, usize)> = None;
for line in text.lines() {
let in_fence = fence.is_some();
if let Some((ch, len)) = fence {
if fence_of(line).is_some_and(|(c, n)| {
c == ch && n >= len && line.trim_start().trim_start_matches(c).trim().is_empty()
}) {
fence = None;
}
} else if let Some(f) = fence_of(line) {
fence = Some(f);
}
let key = if !in_fence && line.contains('|') && is_separator_row(line) {
continue;
} else if !in_fence && is_table_line(line) {
split_cells(line.trim()).join("\u{1}")
} else {
line.split_whitespace().collect::<Vec<_>>().join(" ")
};
let key = normalise_value(&key).replace("**Status**", "**State**");
if !key.is_empty() {
*out.entry(key).or_default() += 1;
}
}
out
}
#[test]
fn canonicalising_is_idempotent_and_loses_no_content() {
let mut checked = 0_usize;
for front in FRONTS {
for (i, a) in FRAGMENTS.iter().enumerate() {
for b in FRAGMENTS.iter().skip(i) {
let doc = format!("{front}{a}\n{b}");
let once = canonical(&doc);
assert_eq!(
canonical(&once),
once,
"not idempotent for:\n{doc:?}\nfirst pass:\n{once:?}"
);
assert_eq!(
says(&doc),
says(&once),
"content changed for:\n{doc:?}\ninto:\n{once:?}"
);
checked += 1;
}
}
}
assert!(checked > 500, "only {checked} documents generated");
}
}
#[cfg(test)]
mod fifth_round {
use super::*;
#[test]
fn an_empty_frontmatter_block_is_not_deleted() {
for src in ["---\n---\n# T\n", "---\n\n---\n# T\n"] {
let got = canonical(src);
assert!(
got.starts_with("---\n") && got[4..].contains("---\n"),
"frontmatter delimiters lost from {src:?}: {got:?}"
);
}
assert_eq!(canonical("# T\n"), "# T\n");
}
#[test]
fn an_escaped_pipe_at_the_end_of_a_row_survives() {
assert_eq!(split_cells(r"| a \|"), vec![r"a \|"]);
assert!(
!canonical_table_row(r"| a \|").contains(r"\ "),
"the escape was split from its pipe: {}",
canonical_table_row(r"| a \|")
);
assert_eq!(split_cells("| a |"), vec!["a"]);
assert_eq!(split_cells(r"| a \\|"), vec![r"a \\"]);
}
}
#[cfg(test)]
mod sixth_round {
use super::*;
#[test]
fn an_indented_backtick_run_is_not_a_fence() {
assert!(fence_of("```md").is_some());
assert!(fence_of(" ```").is_some(), "three spaces is still markup");
assert!(fence_of(" ```").is_none(), "four spaces is code");
assert!(fence_of("\t```").is_none(), "a tab is code");
let got = canonical_body(" ```\n\n| a |b |\n|---|---|\n", true);
assert!(
got.contains("| a | b |"),
"left fenced by an indented run:\n{got}"
);
}
#[test]
fn a_tab_indented_row_is_code_not_a_table() {
assert!(is_table_line("| a | b |"));
assert!(is_table_line(" | a | b |"));
assert!(!is_table_line(" | a | b |"), "four spaces is code");
assert!(!is_table_line("\t| a | b |"), "a tab is code");
assert_eq!(canonical_body("\t| a |b |\n", true), "\t| a |b |\n");
}
#[test]
fn the_date_key_is_parsed_not_prefix_matched() {
assert_eq!(
normalise_value("last-modified : 2026-9-1"),
"last-modified : 2026-09-01"
);
assert_eq!(
normalise_value("Last-Modified: 2026-9-1"),
"Last-Modified: 2026-09-01"
);
assert_eq!(
normalise_value(" last-modified: 2026-9-1"),
" last-modified: 2026-9-1"
);
assert_eq!(normalise_value("other: 2026-9-1"), "other: 2026-9-1");
}
}
#[cfg(test)]
mod seventh_round {
use super::*;
#[test]
fn an_unmatched_backtick_does_not_hide_the_rest_of_the_row() {
assert_eq!(split_cells("| a ` | b |"), vec!["a `", "b"]);
assert_eq!(split_cells("| a `x|y` | b |"), vec!["a `x|y`", "b"]);
}
#[test]
fn a_doubled_colon_is_content_not_alignment() {
assert!(is_separator_row("| :--- | ---: |"));
assert!(is_separator_row("| :---: |"));
assert!(
!is_separator_row("| ::--- | ---:: |"),
"a data row was read as a separator"
);
assert_eq!(
canonical_body("| a | b |\n|---|---|\n| ::--- | ---:: |\n", true),
"| a | b |\n|---|---|\n| ::--- | ---:: |\n"
);
}
#[test]
fn a_later_row_of_the_summary_table_keeps_its_own_status() {
let doc = "---\ntype: adr\n---\n\n| **Status** | Accepted |\n|---|---|\n\
| **Status** | what it means |\n";
let got = canonical(doc);
assert!(got.contains("| **State** | Accepted |"), "{got}");
assert!(
got.contains("| **Status** | what it means |"),
"a data row in the summary table was rewritten:\n{got}"
);
}
}
#[cfg(test)]
mod line_endings {
use super::*;
#[test]
fn a_crlf_document_is_a_fixed_point_and_stays_crlf() {
let src = "---\r\nversion: \"1.0\"\r\nTitle: T\r\n---\r\n\r\n| a |b |\r\n|---|---|\r\n";
let once = canonical(src);
assert_eq!(canonical(&once), once, "not idempotent:\n{once:?}");
assert!(!once.contains('\n') || once.contains("\r\n"), "{once:?}");
assert!(
!once.replace("\r\n", "").contains('\n'),
"line endings were rewritten to LF: {once:?}"
);
assert!(
once.starts_with("---\r\nversion: \"1.0\"\r\n"),
"frontmatter was not seen on pass one: {once:?}"
);
assert!(once.contains("| a | b |"), "{once:?}");
}
#[test]
fn an_lf_document_stays_lf() {
let got = canonical("---\nTitle: T\n---\n\n| a | b |\n|---|---|\n");
assert!(!got.contains('\r'), "{got:?}");
}
}
#[cfg(test)]
mod frontmatter_is_not_rewritten {
use super::*;
#[test]
fn every_frontmatter_shape_survives_except_the_date() {
for src in [
"---\ntags:\n- one\n- two\nstatus: deprecated\nTitle: T\n---\n\n# T\n",
"---\ntags:\n - one\n\n # note\n - two\nstatus: x\nTitle: T\n---\n\n# T\n",
"---\n\n---\n\n# T\n",
"---\n# top matter\nversion: \"1\"\nTitle: T\n# trailing\n---\n\n# T\n",
"---\ndecision-makers: [\"a\", \"b\"]\nsuperseded-by:\n---\n\n# T\n",
"---\nfoo: bar\n\n---\n\n# T\n",
"---\n\nfoo: bar\n\n\n---\n\n# T\n",
] {
let got = canonical(src);
let region = |s: &str| {
let rest = s.strip_prefix("---\n").expect("frontmatter");
let end = rest.find("---\n").unwrap_or(rest.len());
rest[..end].to_owned()
};
assert_eq!(region(src), region(&got), "frontmatter changed for {src:?}");
assert_eq!(canonical(&got), got, "not idempotent for {src:?}");
}
}
#[test]
fn a_date_is_still_padded_in_place() {
let got = canonical("---\nTitle: T\nlast-modified: 2026-9-1\nversion: \"1\"\n---\n\n# T\n");
let (front, _) = crate::adr::split_frontmatter(&got);
assert_eq!(
front, "Title: T\nlast-modified: 2026-09-01\nversion: \"1\"",
"the date moved or its neighbours did"
);
}
}
#[cfg(test)]
mod twelfth_round {
use super::*;
#[test]
fn an_indented_table_keeps_its_indentation() {
let got = canonical_body(" | a |b |\n |---|---|\n", true);
assert_eq!(got, " | a | b |\n |---|---|\n", "{got:?}");
assert_eq!(
canonical_body("| a |b |\n|---|---|\n", true),
"| a | b |\n|---|---|\n"
);
}
#[test]
fn an_escaped_backtick_does_not_open_a_span() {
let cells = split_cells(r"| a \` | b `code` | c |");
assert_eq!(cells.len(), 3, "{cells:?}");
assert_eq!(cells[0], r"a \`");
assert_eq!(cells[1], "b `code`");
}
}
#[cfg(test)]
mod fourteenth_round {
use super::*;
#[test]
fn the_state_row_is_found_below_the_header() {
let doc = "---\ntype: adr\n---\n\n# T\n\n| | |\n|---|---|\n\
| **Status** | Accepted |\n| **Domain** | X |\n";
let got = canonical(doc);
assert!(
got.contains("| **State** | Accepted |"),
"relabel never fired:\n{got}"
);
assert_eq!(canonical(&got), got, "not idempotent:\n{got}");
}
#[test]
fn a_later_status_row_is_left_alone_either_way() {
let doc = "---\ntype: adr\n---\n\n| | |\n|---|---|\n\
| **State** | Accepted |\n| **Status** | what it means |\n";
let got = canonical(doc);
assert!(
got.contains("| **Status** | what it means |"),
"a data row was relabelled:\n{got}"
);
assert_eq!(canonical(&got), got, "not idempotent:\n{got}");
}
#[test]
fn a_backtick_in_an_info_string_is_not_a_fence() {
assert!(fence_of("```rust").is_some());
assert!(fence_of("```bad`").is_none(), "CommonMark §4.5");
assert!(
fence_of("~~~ok`").is_some(),
"only backtick fences are restricted"
);
let got = canonical_body("```bad`\n\n| a |b |\n|---|---|\n", true);
assert!(got.contains("| a | b |"), "left fenced:\n{got}");
}
#[test]
fn a_quoted_date_is_padded_and_stays_quoted() {
assert_eq!(
normalise_value("last-modified: \"2026-9-1\""),
"last-modified: \"2026-09-01\""
);
assert_eq!(
normalise_value("last-modified: '2026-9-1'"),
"last-modified: '2026-09-01'"
);
assert_eq!(
normalise_value("last-modified: 2026-9-1"),
"last-modified: 2026-09-01"
);
}
}
#[cfg(test)]
mod fifteenth_round {
use super::*;
#[test]
fn the_state_row_is_found_when_the_table_is_indented() {
assert!(is_state_row(" | **Status** | Accepted |"));
assert_eq!(
relabel_state(" | **Status** | Accepted |"),
" | **State** | Accepted |"
);
}
#[test]
fn a_crlf_diff_keeps_its_carriage_returns() {
let before = "a\r\nb\r\n";
let after = "a\r\nc\r\n";
let d = unified_diff("x.md", before, after).expect("changed");
assert!(d.contains("-b\r\n"), "context lost its CR:\n{d:?}");
assert!(d.contains("+c\r\n"), "{d:?}");
}
#[test]
fn the_general_path_marks_a_missing_final_newline() {
let d = unified_diff("x.md", "a\nb", "a\nc").expect("changed");
assert_eq!(
d.matches("\\ No newline at end of file").count(),
2,
"both sides end without one:\n{d}"
);
let d = unified_diff("x.md", "a\nb\n", "a\nc\n").expect("changed");
assert!(!d.contains("No newline"), "{d}");
}
}
#[cfg(test)]
mod sixteenth_round {
use super::*;
#[test]
fn a_delimiter_needs_a_header_above_it() {
assert_eq!(
canonical_body("Some prose.\n\n| --- | --- |\n\nMore.\n", true),
"Some prose.\n\n| --- | --- |\n\nMore.\n"
);
assert_eq!(
canonical_body("| a |b |\n| --- | --- |\n", true),
"| a | b |\n|---|---|\n"
);
}
#[test]
fn an_insertion_into_an_empty_file_has_no_phantom_deletion() {
let d = unified_diff("a.md", "", "added\n").expect("changed");
assert!(d.contains("@@ -0,0 +1,1 @@"), "{d}");
assert!(!d.contains("\n-"), "a deletion was invented:\n{d}");
}
}
#[cfg(test)]
mod seventeenth_round {
use super::*;
#[test]
fn the_delimiter_must_immediately_follow_the_header() {
assert_eq!(
canonical_body("| a |\n| b |\n| --- |\n", true),
"| a |\n| b |\n| --- |\n"
);
assert_eq!(canonical_body("| a |\n| --- |\n", true), "| a |\n|---|\n");
}
#[test]
fn a_frontmatter_fence_at_eof_gains_no_newline() {
assert_eq!(canonical("---\nTitle: T\n---"), "---\nTitle: T\n---");
assert_eq!(canonical("---\nTitle: T\n---\n"), "---\nTitle: T\n---\n");
}
}