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) {
let re = Regex::new(r"(?s)```(py|python)\r?\n(.*?)```").unwrap();
if !re.is_match(content) {
return (false, content.to_string());
}
let rendered = re
.replace_all(content, |caps: ®ex::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)
}
}