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