mdbook-trunk 1.0.2

mdBook plugin which bundles packages using Trunk and includes them as iframes.
Documentation
use std::ops::Range;

use anyhow::Result;
use html_parser::{Dom, Node};
use htmlentity::entity::{ICodedDataTrait, decode};
use log::debug;
use mdbook_core::book::Chapter;
use mdbook_plugin_utils::markdown::parse_blocks;
use pulldown_cmark::{Event, TagEnd};

use crate::config::Config;

fn is_iframe_start(event: &Event) -> bool {
    match event {
        Event::Html(html) => html.contains("<iframe data-mdbook-trunk=\""),
        _ => false,
    }
}

fn is_iframe_end(event: &Event) -> bool {
    matches!(event, Event::End(TagEnd::HtmlBlock))
}

pub fn parse_iframes(chapter: &Chapter) -> Result<Vec<(Range<usize>, Config)>> {
    let mut configs: Vec<(Range<usize>, Config)> = vec![];

    let blocks = parse_blocks(&chapter.content, is_iframe_start, is_iframe_end, false)?;
    debug!("{blocks:?}");

    for block in blocks {
        let dom = Dom::parse(&chapter.content[block.span.clone()])?;
        let element = dom
            .children
            .iter()
            .find_map(|child| match child {
                Node::Element(element) if element.name == "iframe" => Some(element),
                _ => None,
            })
            .expect("HTML content should have an iframe.");

        let config = Config::parse_from_json(
            &decode(
                element
                    .attributes
                    .get("data-mdbook-trunk")
                    .expect("Iframe should have config attribute.")
                    .as_ref()
                    .expect("Config attribute should have value.")
                    .as_bytes(),
            )
            .to_string()?,
        )?;
        configs.push((block.span, config));
    }

    debug!("{configs:?}");

    Ok(configs)
}