mdbook_pagetoc/
lib.rs

1use chrono::Local;
2use env_logger::Builder;
3use log::info;
4use log::LevelFilter;
5use mdbook::book::Book;
6use mdbook::errors::Error;
7use mdbook::preprocess::{Preprocessor, PreprocessorContext};
8use std::env;
9use std::fs;
10use std::io::Write;
11use std::str;
12
13fn init_logger() {
14    let mut builder = Builder::new();
15
16    builder.format(|formatter, record| {
17        writeln!(
18            formatter,
19            "{} [{}] ({}): {}",
20            Local::now().format("%Y-%m-%d %H:%M:%S"),
21            record.level(),
22            record.target(),
23            record.args()
24        )
25    });
26
27    if let Ok(var) = env::var("RUST_LOG") {
28        builder.parse_filters(&var);
29    } else {
30        // if no RUST_LOG provided, default to logging at the Info level
31        builder.filter(None, LevelFilter::Info);
32        // Filter extraneous html5ever not-implemented messages
33        builder.filter(Some("html5ever"), LevelFilter::Error);
34    }
35
36    builder.init();
37}
38pub mod pagetoc_lib {
39    use super::*;
40
41    pub struct PagetocPreprocessor;
42
43    impl Default for PagetocPreprocessor {
44        fn default() -> Self {
45            Self::new()
46        }
47    }
48
49    impl PagetocPreprocessor {
50        pub fn new() -> PagetocPreprocessor {
51            PagetocPreprocessor
52        }
53    }
54
55    impl Preprocessor for PagetocPreprocessor {
56        fn name(&self) -> &str {
57            "mdbook-pagetoc"
58        }
59
60        fn run(&self, ctx: &PreprocessorContext, book: Book) -> Result<Book, Error> {
61            init_logger();
62            let html_config = ctx.config.html_config().unwrap_or_default();
63
64            let pagetoc_js = include_str!("pagetoc.js");
65            let pagetoc_css = include_str!("pagetoc.css");
66            let theme_dir = match html_config.theme {
67                Some(ref theme) => ctx.root.join(theme),
68                None => ctx.root.join("theme"),
69            };
70
71            fs::create_dir_all(theme_dir.as_path()).expect("Unable to create directory");
72            for (file_name, contents) in [("pagetoc.js", pagetoc_js), ("pagetoc.css", pagetoc_css)]
73            {
74                let file_path = theme_dir.join(file_name);
75                if !file_path.exists() {
76                    info!("{}: Writing {}", self.name(), file_path.display());
77                    fs::write(file_path, contents).expect("Unable to write file");
78                }
79            }
80            Ok(book)
81        }
82
83        fn supports_renderer(&self, renderer: &str) -> bool {
84            renderer == "html"
85        }
86    }
87}