pub mod error;
mod fragments;
mod preprocess;
use crate::error::Error;
use fs_err as fs;
use std::collections::HashMap;
use std::path::Path;
use std::path::PathBuf;
use mdbook::book::{Book, BookItem, Chapter};
use mdbook::preprocess::{Preprocessor, PreprocessorContext};
use nom_bibtex::*;
use preprocess::{replace_blocks, replace_inline_blocks};
pub struct Scientific;
impl Scientific {
pub fn new() -> Scientific {
Scientific
}
}
impl Preprocessor for Scientific {
fn name(&self) -> &str {
"scientific"
}
fn supports_renderer(&self, renderer: &str) -> bool {
renderer != "not-supported"
}
fn run(&self, ctx: &PreprocessorContext, book: Book) -> Result<Book, mdbook::errors::Error> {
self.run_inner(ctx, book)
.map_err(mdbook::errors::Error::new)
}
}
impl Scientific {
fn run_inner(&self, ctx: &PreprocessorContext, mut book: Book) -> crate::error::Result<Book> {
if let Some(cfg) = ctx.config.get_preprocessor(self.name()) {
let fragment_path = cfg
.get("fragment_path")
.map(|x| x.as_str().expect("Fragment path is valid UTF8. qed"))
.unwrap_or("fragments/");
let fragment_path = Path::new(fragment_path);
fs::create_dir_all(fragment_path)?;
let fragment_path = fs::canonicalize(fragment_path)?;
let mut used_fragments = Vec::new();
let mut references = HashMap::new();
let mut error = Ok::<_, Error>(());
if let (Some(bib), Some(bib2xhtml)) = (cfg.get("bibliography"), cfg.get("bib2xhtml")) {
let bib = bib.as_str().unwrap();
let bib2xhtml = bib2xhtml.as_str().expect("bib string is valid UTF8. qed");
if !Path::new(bib).exists() {
return Err(Error::BibliographyMissing(bib.to_owned()));
}
let bibtex = fs::read_to_string(bib)?;
let bibtex = Bibtex::parse(&bibtex)?;
for (i, entry) in bibtex.bibliographies().into_iter().enumerate() {
references.insert(entry.citation_key().to_string(), format!("[{}]", i + 1));
}
let content = fragments::bib_to_html(&bib, &bib2xhtml)?;
let bib_chapter = Chapter::new(
"Bibliography",
format!("# Bibliography\n{}", content),
PathBuf::from("bibliography.md"),
Vec::new(),
);
book.push_item(bib_chapter);
}
let asset_path = cfg
.get("assets")
.map(|x| x.as_str().expect("Assumes valid UTF8 for assets. qed"))
.unwrap_or("src/");
let asset_path = ctx.root.join(asset_path);
book.for_each_mut(|item| {
if let Err(_) = error {
return;
}
if let BookItem::Chapter(ref mut ch) = item {
let head_number = ch
.number
.as_ref()
.map(|x| x.to_string())
.unwrap_or(String::new());
match replace_blocks(
&fragment_path,
&asset_path,
&ch.content,
&head_number,
&mut used_fragments,
&mut references,
) {
Ok(x) => ch.content = x,
Err(err) => error = Err(Error::from(err)),
}
}
});
book.for_each_mut(|item| {
if error.is_err() {
return;
}
if let BookItem::Chapter(ref mut ch) = item {
let _head_number = ch
.number
.as_ref()
.map(|x| format!("{}", x))
.unwrap_or("".into());
match replace_inline_blocks(
&fragment_path,
&ch.content,
&references,
&mut used_fragments,
) {
Ok(x) => ch.content = x,
Err(err) => error = Err(Error::from(err)),
}
}
});
error?;
let dest = ctx.root.join("src").join("assets");
if !dest.exists() {
fs::create_dir_all(&dest)?;
}
for fragment in used_fragments {
fs::copy(fragment_path.join(&fragment), dest.join(&fragment))?;
}
Ok(book)
} else {
Err(Error::KeySectionNotFound)
}
}
}