use fancy_regex::{Captures, Regex};
use lazy_static::lazy_static;
use mdbook::book::{Book, BookItem};
use mdbook::errors::Result;
use mdbook::preprocess::{Preprocessor, PreprocessorContext};
const NAME: &str = "mathpunc";
pub struct MathpuncPreprocessor;
lazy_static! {
static ref RE: Regex =
Regex::new(r"(?<!\\)\$\s*(?<punc>\)?[,,.,;,:,)])").unwrap();
}
impl MathpuncPreprocessor {
pub fn new() -> Self {
Self
}
}
impl Preprocessor for MathpuncPreprocessor {
fn name(&self) -> &str {
NAME
}
fn run(&self, _ctx: &PreprocessorContext, mut book: Book) -> Result<Book> {
book.for_each_mut(|item: &mut BookItem| {
if let BookItem::Chapter(chapter) = item {
chapter.content = find_and_replace(&chapter.content);
}
});
Ok(book)
}
}
fn find_and_replace(s: &str) -> String {
RE.replace_all(s, |caps: &Captures| {
match &caps["punc"] {
":" => {
r"\!\!:$".to_string()
}
"):" => {
r")\!\!:$".to_string()
}
_ => {
format!("{}$", &caps["punc"])
}
}
}).to_string()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn basic() {
let input = String::from(
r"Consider a group $\GG$, of order $p$; and a generator $G$: for example an elliptic curve $E$.",
);
let output = find_and_replace(&input);
let expected = String::from(
r"Consider a group $\GG,$ of order $p;$ and a generator $G\!\!:$ for example an elliptic curve $E.$",
);
assert_eq!(output, expected);
}
#[test]
fn escaped_dollar() {
let input = String::from(r"This is an escaped dollar \$, don't replace. This as well \& .");
let output = find_and_replace(&input);
assert_eq!(output, input);
}
#[test]
fn whitespaces() {
let input = String::from(
r"Consider a group $\GG$ , of order $p$ ; and a generator $G$ : for example an elliptic curve $E$ .",
);
let output = find_and_replace(&input);
let expected = String::from(
r"Consider a group $\GG,$ of order $p;$ and a generator $G\!\!:$ for example an elliptic curve $E.$",
);
assert_eq!(output, expected);
}
#[test]
fn parenthesis() {
let input =
String::from(r"Consider a group $\GG$ (of order $p$), and a generator $G$ (of $\GG$).");
let output = find_and_replace(&input);
let expected =
String::from(r"Consider a group $\GG$ (of order $p),$ and a generator $G$ (of $\GG).$");
assert_eq!(output, expected);
}
#[test]
fn parenthesis_and_colon() {
let input =
String::from(r"Consider a group $\GG$ (of order $p$):");
let output = find_and_replace(&input);
let expected =
String::from(r"Consider a group $\GG$ (of order $p)\!\!:$");
assert_eq!(output, expected);
}
}