mod anchor;
mod frontmatter;
mod link;
mod options;
mod roundtrip;
mod table;
pub use options::RefineOptions;
pub fn refine(markdown: &str, options: &RefineOptions) -> String {
let _ = options;
let (frontmatter, content) = frontmatter::extract(markdown);
let recovered = table::recover_missing_separators(content);
let widened = table::widen_ragged_tables(&recovered);
let events = anchor::assign_section_anchors(link::normalize_link_destinations(
roundtrip::parse(&widened),
));
let Some(refined_body) = roundtrip::serialize(events) else {
return markdown.to_string();
};
match frontmatter {
Some(fm) => format!(
"{}\n{}",
frontmatter::normalize(fm),
refined_body.trim_start()
),
None => refined_body,
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn round_trips_plain_markdown() {
let options = RefineOptions::default();
assert_eq!(refine("hello world\n", &options), "hello world");
}
#[test]
fn preserves_frontmatter_verbatim() {
let options = RefineOptions::default();
let input = "---\ntitle: x\n---\n\nhello\n";
let output = refine(input, &options);
assert!(
output.starts_with("---\ntitle: x\n---\n"),
"frontmatter not preserved verbatim: {output:?}"
);
}
#[test]
fn drops_an_exact_duplicate_frontmatter_entry() {
let options = RefineOptions::default();
let input = "---\ntitle: \"x\"\ntitle: \"x\"\n---\n\nhello\n";
let output = refine(input, &options);
assert_eq!(
output.matches("title:").count(),
1,
"exact duplicate entry not deduped: {output:?}"
);
}
#[test]
fn keeps_a_same_key_entry_with_a_different_value() {
let options = RefineOptions::default();
let input = "---\ntitle: \"x\"\ntitle: \"y\"\n---\n\nhello\n";
let output = refine(input, &options);
assert!(
output.contains("\"x\"") && output.contains("\"y\""),
"a non-duplicate value was dropped: {output:?}"
);
}
#[test]
fn assigns_a_section_anchor_to_a_heading() {
let options = RefineOptions::default();
let output = refine("# Getting Started\n", &options);
assert!(
output.contains("{ #getting-started }"),
"no anchor assigned: {output:?}"
);
}
#[test]
fn round_trips_a_table() {
let options = RefineOptions::default();
let input = "| a | b |\n| --- | --- |\n| 1 | 2 |\n";
let output = refine(input, &options);
for token in ["a", "b", "1", "2"] {
assert!(output.contains(token), "missing {token:?} in {output:?}");
}
}
#[test]
fn recovers_a_table_missing_its_separator_row() {
let options = RefineOptions::default();
let input = "| a | b |\n| 1 | 2 |\n";
let output = refine(input, &options);
assert!(
!output.contains(r"\|"),
"pipes were escaped, meaning the block was not recognized as a table: {output:?}"
);
for token in ["a", "b", "1", "2"] {
assert!(output.contains(token), "missing {token:?} in {output:?}");
}
}
#[test]
fn preserves_a_row_wider_than_the_header_by_widening_it() {
let options = RefineOptions::default();
let input = "| a | b |\n| --- | --- |\n| 1 | 2 | hello |\n";
let output = refine(input, &options);
assert!(output.contains("hello"), "excess cell was lost: {output:?}");
}
#[test]
fn pads_a_short_data_row_with_an_empty_cell() {
let options = RefineOptions::default();
let input = "| a | b | c |\n| --- | --- | --- |\n| 1 | 2 |\n";
let output = refine(input, &options);
let data_row = output
.lines()
.find(|line| line.contains('1'))
.expect("data row present");
assert_eq!(
data_row.matches('|').count(),
4,
"expected a padded 3-cell row (4 pipes): {data_row:?}"
);
}
}