mdtablefix 0.6.1

`mdtablefix` unb0rks and reflows Markdown tables so that each column has a uniform width. When the `--wrap` option is used, it also wraps paragraphs and list items to 80 columns.
Documentation
//! Preserves repaired table lines while non-table code-emphasis is processed.
//!
//! Table cells receive code-emphasis repair before reflow, so their final text
//! must not enter the later non-table repair pass. This module replaces table
//! lines with collision-resistant placeholders after heading conversion, then
//! restores them afterwards.

use std::collections::HashMap;

/// Placeholder mapping that keeps already reflowed table rows out of later
/// non-table transformations.
pub(super) struct ProtectedTableLines {
    /// Collision-resistant placeholder to original table-line mapping.
    replacements: HashMap<String, String>,
}

/// Replaces table rows with unique placeholders before a later text pass.
///
/// Duplicate table rows are consumed in source order, and the generated
/// placeholder prefix is selected not to occur in the existing text.
pub(super) fn protect_table_lines(
    mut lines: Vec<String>,
    table_lines: &[String],
) -> (Vec<String>, ProtectedTableLines) {
    let mut nonce = 0_usize;
    let placeholder_prefix = loop {
        let candidate = format!("\u{E000}mdtablefix-table-{nonce}\u{E001}");
        if lines.iter().all(|line| !line.contains(&candidate)) {
            break candidate;
        }
        nonce += 1;
    };

    let mut table_line_counts: HashMap<&str, usize> = HashMap::new();
    for table_line in table_lines {
        *table_line_counts
            .entry(table_line.as_str())
            .or_insert(0_usize) += 1;
    }

    let mut replacements = HashMap::new();
    for (index, line) in lines.iter_mut().enumerate() {
        if let Some(remaining) = table_line_counts.get_mut(line.as_str()) {
            if *remaining == 0 {
                continue;
            }
            *remaining -= 1;
            let placeholder = format!("{placeholder_prefix}{index}");
            let table_line = std::mem::replace(line, placeholder.clone());
            replacements.insert(placeholder, table_line);
        }
    }

    (lines, ProtectedTableLines { replacements })
}

/// Restores protected table rows after non-table transformations finish.
///
/// Lines without a placeholder are passed through unchanged, which allows the
/// surrounding processing stages to add or remove ordinary rows safely.
pub(super) fn restore_table_lines(
    lines: Vec<String>,
    protected_table_lines: &ProtectedTableLines,
) -> Vec<String> {
    lines
        .into_iter()
        .map(|line| {
            protected_table_lines
                .replacements
                .get(&line)
                .cloned()
                .unwrap_or(line)
        })
        .collect()
}