mdbook_merjong/
preprocessor.rs1use mdbook::{
2 book::{Book, BookItem},
3 errors::Result as MdbookResult,
4 preprocess::{Preprocessor, PreprocessorContext},
5};
6use pulldown_cmark::{CodeBlockKind::*, Event, Options, Parser, Tag, TagEnd};
7
8pub struct Merjong;
9
10impl Preprocessor for Merjong {
11 fn name(&self) -> &str {
12 "merjong"
13 }
14 fn run(&self, _ctx: &PreprocessorContext, mut book: Book) -> MdbookResult<Book> {
15 let mut res = None;
16 book.for_each_mut(|item: &mut BookItem| {
17 if let Some(Err(_)) = res {
18 return;
19 }
20 if let BookItem::Chapter(ref mut chapter) = *item {
21 res = Some(preprocess(&chapter.content).map(|md| {
22 chapter.content = md;
23 }));
24 }
25 });
26 res.unwrap_or(Ok(())).map(|_| book)
27 }
28 fn supports_renderer(&self, _renderer: &str) -> bool {
29 true
30 }
31}
32
33fn preprocess(content: &str) -> MdbookResult<String> {
34 let mut merjong_content = String::new();
35 let mut in_merjong_block = false;
36
37 let options = Options::empty();
38
39 let mut code_span = 0..0;
40 let mut start_new_code_span = true;
41
42 let mut merjong_blocks = vec![];
43
44 let events = Parser::new_ext(content, options);
45 for (event, span) in events.into_offset_iter() {
46 if let Event::Start(Tag::CodeBlock(Fenced(code))) = &event {
47 if code.as_ref() == "merjong" {
48 in_merjong_block = true;
49 merjong_content.clear();
50 }
51 }
52
53 if !in_merjong_block {
54 continue;
55 }
56
57 if let Event::Text(_) = event {
58 if start_new_code_span {
59 code_span = span;
60 start_new_code_span = false;
61 } else {
62 code_span = code_span.start..span.end;
63 }
64
65 continue;
66 }
67
68 if let Event::End(TagEnd::CodeBlock) = &event {
69 in_merjong_block = false;
70
71 let merjong_content = &content[code_span.clone()]
72 .replace("\r\n", "\n")
73 .trim_end()
74 .to_string();
75 let merjong_code = format!("<pre class=\"merjong\">{}</pre>", merjong_content);
76 merjong_blocks.push((span, merjong_code));
77 start_new_code_span = true;
78 }
79 }
80
81 let mut new_content = content.to_string();
82
83 for (span, block) in merjong_blocks.iter().rev() {
84 let pre_content = &new_content[..span.start];
85 let post_content = &new_content[span.end..];
86 new_content = format!("{}{}{}", pre_content, block, post_content);
87 }
88
89 Ok(new_content)
90}
91
92#[cfg(test)]
93mod tests {
94 use super::*;
95 #[test]
96
97 fn test_preprocess() {
98 let content = r#"
99# Chapter 1
100
101```merjong
102234m-234m-234m-222p-5m-5m
103````
104
105"#;
106
107 let result = preprocess(content).expect("preprocess failed");
108
109 println!("test_preprocess' s Result: {}", result);
110 }
111}