mdbook_regex_replacer/
lib.rs

1use log::{debug};
2use mdbook::preprocess::{Preprocessor, PreprocessorContext, CmdPreprocessor};
3use mdbook::book::Book;
4use mdbook::errors::Error;
5use clap::ArgMatches;
6use std::{process, io};
7use mdbook::BookItem;
8use regex::Regex;
9use serde_derive::{Deserialize, Serialize};
10
11
12#[macro_use]
13extern crate lazy_static;
14
15
16lazy_static! {
17    /// if you use mermaid, may be use `Flow1 ==description==> Flow2`, this string will ignore
18    static ref RE : Regex= Regex::new(r"==(?P<c>\S+?)==[^>]").unwrap();
19}
20
21#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
22#[serde(default, rename_all = "kebab-case")]
23pub struct RegexReplacerItem {
24    regex: String,
25    rep: String
26}
27
28impl Default for RegexReplacerItem {
29    fn default() -> Self {
30        RegexReplacerItem{
31            regex:"".to_string(),
32            rep:"".to_string(),
33        }
34    }
35}
36
37#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
38#[serde(default, rename_all = "kebab-case")]
39pub struct RegexReplacerConfigure {
40    command: Option<String>,
41    items: Option<Vec<RegexReplacerItem>>,
42}
43
44impl Default for RegexReplacerConfigure{
45    fn default() -> Self {
46        RegexReplacerConfigure{command: Option::None, items: Option::None}
47    }
48}
49
50#[test]
51fn test_replace(){
52    let c = (Regex::new("==(?P<c>.+?)==").unwrap(), "<mark>$c</mark>".to_string());
53    let f = replace_all(&c, "==sasasas== s");
54    print!("{}", f);
55}
56
57pub fn replace_all(e: & (Regex, String), s: &str) -> String {
58    e.0.replace_all(s, e.1.as_str()).into_owned()
59}
60
61pub fn handle_each_item(book_item: &mut BookItem, regexes: & Vec<(Regex, String)>) {
62    match book_item {
63        BookItem::Chapter(chapter) => {
64
65            for e in regexes {
66                chapter.content = replace_all(e, chapter.content.as_str());
67                debug!("after regex placer: {} => {}:\n{}", e.0, e.1, chapter.content);
68            }
69
70            for item in &mut chapter.sub_items {
71                handle_each_item(item, regexes);
72            }
73        }
74        _ => {}
75    }
76}
77
78pub struct RegexReplacerPreprocessor {}
79
80impl Preprocessor for RegexReplacerPreprocessor {
81
82    fn name(&self) -> &str {
83        "regex-replacer"
84    }
85
86    fn run(&self, ctx: &PreprocessorContext, mut book: Book) -> Result<Book, Error> {
87
88        let config = ctx.config.get_preprocessor(self.name()).unwrap();
89        let config_string = toml::to_string(config).unwrap();
90        let regex_replacer_configure: RegexReplacerConfigure = toml::from_str(config_string.as_str()).unwrap();
91
92        let mut regexes: Vec<(Regex, String)> = Vec::new();
93
94        if let Some(items) = &regex_replacer_configure.items {
95            for e in items{
96                let regex = Regex::new(e.regex.as_str()).unwrap();
97                regexes.push((regex, e.rep.clone()));
98            }
99        }
100
101        let ii = &mut book.sections;
102        for section in ii {
103            handle_each_item(section, & regexes);
104        }
105        Ok(book)
106    }
107
108    fn supports_renderer(&self, _renderer: &str) -> bool {
109        _renderer == "html"
110    }
111}
112
113pub fn handle_preprocessor(pre: &dyn Preprocessor) -> Result<(), Error> {
114    debug!("mark start");
115    let (ctx, book) = CmdPreprocessor::parse_input(io::stdin())?;
116
117    if ctx.mdbook_version != mdbook::MDBOOK_VERSION {
118        // We should probably use the `semver` crate to check compatibility
119        // here...
120        eprintln!(
121            "Warning: The {} plugin was built against version {} of mdbook, \
122             but we're being called from version {}",
123            pre.name(),
124            mdbook::MDBOOK_VERSION,
125            ctx.mdbook_version
126        );
127    }
128
129    let processed_book = pre.run(&ctx, book)?;
130
131    serde_json::to_writer(io::stdout(), &processed_book)?;
132
133    Ok(())
134}
135
136pub fn handle_supports(pre: &dyn Preprocessor, sub_args: &ArgMatches) -> ! {
137    let renderer = sub_args.value_of("renderer").expect("Required argument");
138    let supported = pre.supports_renderer(&renderer);
139
140    // Signal whether the renderer is supported by exiting with 1 or 0.
141    if supported {
142        process::exit(0);
143    } else {
144        process::exit(1);
145    }
146}