mdbook_plotly/preprocessor/
handlers.rs1pub mod code_handler;
2#[cfg(feature = "plotly-html-handler")]
3pub mod plotly_html_handler;
4#[cfg(feature = "plotly-svg-handler")]
5pub mod plotly_svg_handler;
6
7use crate::preprocessor::config::{PlotlyOutputType, PreprocessorConfig};
8use anyhow::Result;
9use log::{error, warn};
10use mdbook_preprocessor::book::Chapter;
11use pulldown_cmark::{CodeBlockKind, Event, Parser, Tag, TagEnd};
12use std::path::Path;
13
14const PULLDOWN_CMARK_OPTIONS: pulldown_cmark::Options = pulldown_cmark::Options::all();
15
16pub fn handle(chapter: &mut Chapter, config: &PreprocessorConfig, book_path: &Path) {
17 let events = Parser::new_ext(&chapter.content, PULLDOWN_CMARK_OPTIONS);
18
19 let mut code = String::with_capacity(10);
20 let mut in_target_code = false;
21 let mut new_events = Vec::with_capacity(10);
22
23 if config.output_type == PlotlyOutputType::PlotlyHtml {
24 new_events.push(plotly_html_handler::inject_header(
25 config.offline_js_sources,
26 ));
27 new_events.push(Event::HardBreak);
28 }
29
30 for event in events {
31 match event {
32 Event::Start(Tag::CodeBlock(CodeBlockKind::Fenced(ref lang))) => {
33 if matches!(lang.as_ref(), "plotly" | "plot") {
34 in_target_code = true;
35 code.clear();
36 } else {
37 new_events.push(event);
38 }
39 }
40 Event::Text(ref text) => {
41 if in_target_code {
42 code.push_str(text);
43 } else {
44 new_events.push(event);
45 }
46 }
47 Event::End(TagEnd::CodeBlock) => {
48 if !in_target_code {
49 new_events.push(event);
50 continue;
51 }
52 in_target_code = false;
53 let ready_code = std::mem::take(&mut code);
54 match handle_plotly(ready_code, config, book_path) {
55 Ok(event) => {
56 new_events.push(Event::HardBreak);
57 new_events.push(event);
58 new_events.push(Event::HardBreak);
59 }
60 Err(message) => warn!(
61 "An error occurred during processing in Chapter {}:\n{}",
62 chapter.name, message
63 ),
64 }
65 }
66 _ => new_events.push(event),
67 }
68 }
69 let mut new_content = String::with_capacity(chapter.content.len());
70 if let Err(e) = pulldown_cmark_to_cmark::cmark_with_options(
71 new_events.into_iter(),
72 &mut new_content,
73 pulldown_cmark_to_cmark::Options::default(),
74 ) {
75 error!(
76 "Processing failed during cmark. {} Chapter will use the original content. \nInterError: {:#?}",
77 chapter.name, e
78 );
79 } else {
80 chapter.content = new_content;
81 }
82}
83
84#[allow(unused)]
85pub fn handle_plotly<'a>(
86 code: String,
87 config: &'a PreprocessorConfig,
88 book_path: &'a Path,
89) -> Result<Event<'static>> {
90 let plot = code_handler::handle(code, &config.input_type)?;
91 let result = match config.output_type {
92 #[cfg(feature = "plotly-html-handler")]
93 PlotlyOutputType::PlotlyHtml => plotly_html_handler::handle(plot),
94 #[cfg(feature = "plotly-svg-handler")]
95 PlotlyOutputType::PlotlySvg => plotly_svg_handler::handle(plot, book_path),
96 };
97 Ok(result)
98}