mdbook-repl 0.1.0

A rust based mdbook preprocessor plugin that allows you to live code in your markdown book.
use mdbook::{
    book::Book,
    errors::Error,
    preprocess::{Preprocessor, PreprocessorContext},
};
use regex::Regex;
use rust_embed::RustEmbed;
use uuid::Uuid;

#[derive(RustEmbed)]
#[folder = "assets"]
struct Assets;

pub struct Repl;

impl Repl {
    pub fn new() -> Repl {
        Repl
    }
}

fn get_asset(name: &str) -> String {
    let path = format!("{}", name);
    let file = Assets::get(&path).unwrap();
    std::str::from_utf8(file.data.as_ref()).unwrap().to_string()
}

fn render_repls(content: &str) -> (bool, String) {
    // \r? is for windows line endings
    let re = Regex::new(r"(?s)```(py|python)\r?\n(.*?)```").unwrap();

    // if there are no matches, return the content as is
    if !re.is_match(content) {
        return (false, content.to_string());
    }

    // replace all matches with the repl html
    let rendered = re
        .replace_all(content, |caps: &regex::Captures| {
            let lang = "python";
            let id = Uuid::new_v4().to_string();
            let code = caps.get(0).unwrap().as_str().trim();

            get_asset("repl.html")
                .replace("{id}", &id)
                .replace("{lang}", lang)
                .replace("{code}", &code)
        })
        .to_string();

    (true, rendered)
}

impl Preprocessor for Repl {
    fn name(&self) -> &str {
        "mdbook-repl"
    }

    fn run(&self, _ctx: &PreprocessorContext, mut book: Book) -> Result<Book, Error> {
        book.for_each_mut(|item| {
            if let mdbook::book::BookItem::Chapter(chapter) = item {
                let (repl_found, rendered) = render_repls(&chapter.content);
                if repl_found {
                    chapter.content = rendered;
                    chapter.content.push_str(&get_asset("script.html"));
                    chapter.content.insert_str(0, &get_asset("style.html"));
                }
            }
        });
        Ok(book)
    }
}