This moves Result and Error to mdbook-core with the anticipation of using them in user crates. For now, the internal APIs will be using anyhow directly, but the intent is to transition more of these to mdbook-core where it makes sense.
162 lines
5 KiB
Rust
162 lines
5 KiB
Rust
//! A basic example of a preprocessor that does nothing.
|
|
|
|
use crate::nop_lib::Nop;
|
|
use anyhow::Error;
|
|
use clap::{Arg, ArgMatches, Command};
|
|
use mdbook::book::Book;
|
|
use mdbook::preprocess::{CmdPreprocessor, Preprocessor, PreprocessorContext};
|
|
use semver::{Version, VersionReq};
|
|
use std::io;
|
|
use std::process;
|
|
|
|
fn make_app() -> Command {
|
|
Command::new("nop-preprocessor")
|
|
.about("A mdbook preprocessor which does precisely nothing")
|
|
.subcommand(
|
|
Command::new("supports")
|
|
.arg(Arg::new("renderer").required(true))
|
|
.about("Check whether a renderer is supported by this preprocessor"),
|
|
)
|
|
}
|
|
|
|
fn main() {
|
|
let matches = make_app().get_matches();
|
|
|
|
// Users will want to construct their own preprocessor here
|
|
let preprocessor = Nop::new();
|
|
|
|
if let Some(sub_args) = matches.subcommand_matches("supports") {
|
|
handle_supports(&preprocessor, sub_args);
|
|
} else if let Err(e) = handle_preprocessing(&preprocessor) {
|
|
eprintln!("{e:?}");
|
|
process::exit(1);
|
|
}
|
|
}
|
|
|
|
fn handle_preprocessing(pre: &dyn Preprocessor) -> Result<(), Error> {
|
|
let (ctx, book) = CmdPreprocessor::parse_input(io::stdin())?;
|
|
|
|
let book_version = Version::parse(&ctx.mdbook_version)?;
|
|
let version_req = VersionReq::parse(mdbook::MDBOOK_VERSION)?;
|
|
|
|
if !version_req.matches(&book_version) {
|
|
eprintln!(
|
|
"Warning: The {} plugin was built against version {} of mdbook, \
|
|
but we're being called from version {}",
|
|
pre.name(),
|
|
mdbook::MDBOOK_VERSION,
|
|
ctx.mdbook_version
|
|
);
|
|
}
|
|
|
|
let processed_book = pre.run(&ctx, book)?;
|
|
serde_json::to_writer(io::stdout(), &processed_book)?;
|
|
|
|
Ok(())
|
|
}
|
|
|
|
fn handle_supports(pre: &dyn Preprocessor, sub_args: &ArgMatches) -> ! {
|
|
let renderer = sub_args
|
|
.get_one::<String>("renderer")
|
|
.expect("Required argument");
|
|
let supported = pre.supports_renderer(renderer);
|
|
|
|
// Signal whether the renderer is supported by exiting with 1 or 0.
|
|
if supported {
|
|
process::exit(0);
|
|
} else {
|
|
process::exit(1);
|
|
}
|
|
}
|
|
|
|
/// The actual implementation of the `Nop` preprocessor. This would usually go
|
|
/// in your main `lib.rs` file.
|
|
mod nop_lib {
|
|
use super::*;
|
|
|
|
/// A no-op preprocessor.
|
|
pub struct Nop;
|
|
|
|
impl Nop {
|
|
pub fn new() -> Nop {
|
|
Nop
|
|
}
|
|
}
|
|
|
|
impl Preprocessor for Nop {
|
|
fn name(&self) -> &str {
|
|
"nop-preprocessor"
|
|
}
|
|
|
|
fn run(&self, ctx: &PreprocessorContext, book: Book) -> Result<Book, Error> {
|
|
// In testing we want to tell the preprocessor to blow up by setting a
|
|
// particular config value
|
|
if let Some(nop_cfg) = ctx.config.get_preprocessor(self.name()) {
|
|
if nop_cfg.contains_key("blow-up") {
|
|
anyhow::bail!("Boom!!1!");
|
|
}
|
|
}
|
|
|
|
// we *are* a no-op preprocessor after all
|
|
Ok(book)
|
|
}
|
|
|
|
fn supports_renderer(&self, renderer: &str) -> bool {
|
|
renderer != "not-supported"
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod test {
|
|
use super::*;
|
|
|
|
#[test]
|
|
fn nop_preprocessor_run() {
|
|
let input_json = r##"[
|
|
{
|
|
"root": "/path/to/book",
|
|
"config": {
|
|
"book": {
|
|
"authors": ["AUTHOR"],
|
|
"language": "en",
|
|
"multilingual": false,
|
|
"src": "src",
|
|
"title": "TITLE"
|
|
},
|
|
"preprocessor": {
|
|
"nop": {}
|
|
}
|
|
},
|
|
"renderer": "html",
|
|
"mdbook_version": "0.4.21"
|
|
},
|
|
{
|
|
"sections": [
|
|
{
|
|
"Chapter": {
|
|
"name": "Chapter 1",
|
|
"content": "# Chapter 1\n",
|
|
"number": [1],
|
|
"sub_items": [],
|
|
"path": "chapter_1.md",
|
|
"source_path": "chapter_1.md",
|
|
"parent_names": []
|
|
}
|
|
}
|
|
],
|
|
"__non_exhaustive": null
|
|
}
|
|
]"##;
|
|
let input_json = input_json.as_bytes();
|
|
|
|
let (ctx, book) = mdbook::preprocess::CmdPreprocessor::parse_input(input_json).unwrap();
|
|
let expected_book = book.clone();
|
|
let result = Nop::new().run(&ctx, book);
|
|
assert!(result.is_ok());
|
|
|
|
// The nop-preprocessor should not have made any changes to the book content.
|
|
let actual_book = result.unwrap();
|
|
assert_eq!(actual_book, expected_book);
|
|
}
|
|
}
|
|
}
|