h2m 0.3.1

HTML to Markdown converter.
Documentation
//! Built-in `CommonMark` conversion rules.

mod blockquote;
mod code_block;
mod emphasis;
mod heading;
mod horizontal_rule;
mod iframe;
mod image;
mod inline_code;
mod line_break;
mod link;
mod list;
mod paragraph;
mod strong;

use crate::converter::{ConverterBuilder, Plugin};

/// Wraps each non-empty line with `delimiter` so that bold/italic spanning
/// multiple lines is rendered correctly.
///
/// Shared by [`strong::Strong`] and [`emphasis::Emphasis`].
pub(crate) fn wrap_delimiter_per_line(text: &str, delimiter: &str) -> String {
    if !text.contains('\n') {
        return format!("{delimiter}{text}{delimiter}");
    }

    let mut result = String::with_capacity(text.len() + delimiter.len() * 4);
    for (i, line) in text.split('\n').enumerate() {
        if i > 0 {
            result.push('\n');
        }
        let trimmed = line.trim();
        if trimmed.is_empty() {
            continue;
        }
        result.push_str(delimiter);
        result.push_str(trimmed);
        result.push_str(delimiter);
    }
    result
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn wrap_single_line() {
        assert_eq!(wrap_delimiter_per_line("bold", "**"), "**bold**");
    }

    #[test]
    fn wrap_multiline() {
        assert_eq!(
            wrap_delimiter_per_line("line1\nline2", "**"),
            "**line1**\n**line2**"
        );
    }

    #[test]
    fn wrap_multiline_preserves_blank_line() {
        assert_eq!(
            wrap_delimiter_per_line("line1\n\nline2", "**"),
            "**line1**\n\n**line2**"
        );
    }

    #[test]
    fn wrap_single_char_delimiter() {
        assert_eq!(wrap_delimiter_per_line("text", "*"), "*text*");
    }
}

/// The `CommonMark` plugin — registers all built-in rules for standard HTML
/// tags.
///
/// This plugin handles headings, paragraphs, emphasis, strong, inline code,
/// code blocks, links, images, lists, blockquotes, horizontal rules, and
/// line breaks.
#[derive(Debug, Clone, Copy)]
#[allow(clippy::exhaustive_structs)]
pub struct CommonMark;

impl Plugin for CommonMark {
    fn register(&self, builder: &mut ConverterBuilder) {
        builder.add_rule(paragraph::Paragraph);
        builder.add_rule(heading::Heading);
        builder.add_rule(code_block::CodeBlock);
        builder.add_rule(blockquote::Blockquote);
        builder.add_rule(list::List);
        builder.add_rule(list::ListItem);
        builder.add_rule(horizontal_rule::HorizontalRule);
        builder.add_rule(line_break::LineBreak);
        builder.add_rule(strong::Strong);
        builder.add_rule(emphasis::Emphasis);
        builder.add_rule(inline_code::InlineCode);
        builder.add_rule(link::Link);
        builder.add_rule(image::Image);
        builder.add_rule(iframe::Iframe);
    }
}