Macro create_formatter

#[macro_export]
macro_rules! create_formatter {
    ($name:ident, { $( $pat:pat => | $( $capture:ident ),* | $case:tt ),* $(,)? }) => { ... };
    ($name:ident<$type:ty>, { $( $pat:pat => | $( $capture:ident ),* | $case:tt ),* $(,)? }) => { ... };
    (@inner $name:ident<$type:ty $(, $user_type:ty)?>, { $( $pat:pat => | $( $capture:ident ),* | $case:tt ),* $(,)? }) => { ... };
}

Create a formatter with specialised rules for certain node types.

Give the name of the newly created struct, and then a list of NodeValue match cases within curly braces. The left-hand side are regular patterns and can include captures. The right-hand side starts with a mandatory list of contextual captures, similar to lambdas. The following contextual captures are available:

By default, an overridden formatter will return ChildRendering::HTML, causing children of the node to be rendered as HTML as usual. You can return one of these enum values (wrapped in Ok) from within your override to change this behaviour, in some or all cases. These values are only noted when entering a node.

If you supply a type parameter after the name of your formatter, it will be taken as an additional argument on the generated format_document method, is available on the Context as the user property, and becomes the return value of format_document.

# use comrak::{create_formatter, parse_document, Arena, Options, nodes::NodeValue, html::ChildRendering};
# use std::fmt::Write;
create_formatter!(CustomFormatter<usize>, {
    NodeValue::Emph => |context, entering| {
        context.user += 1;
        if entering {
            context.write_str("<i>")?;
        } else {
            context.write_str("</i>")?;
        }
    },
    NodeValue::Strong => |context, entering| {
        context.user += 1;
        context.write_str(if entering { "<b>" } else { "</b>" })?;
    },
    NodeValue::Image(ref nl) => |context, node, entering| {
        assert!(node.data().sourcepos == (3, 1, 3, 18).into());
        if entering {
            context.write_str(&nl.url.to_uppercase())?;
            return Ok(ChildRendering::Skip);
        }
    },
});

let options = Options::default();
let arena = Arena::new();
let doc = parse_document(
    &arena,
    "_Hello_, **world**.\n\n![title](/img.png)",
    &options,
);

let mut result: String = String::new();
let converted_count = CustomFormatter::format_document(doc, &options, &mut result, 0).unwrap();

assert_eq!(
    result,
    "<p><i>Hello</i>, <b>world</b>.</p>\n<p>/IMG.PNG</p>\n"
);

assert_eq!(converted_count, 4);