mirror of
https://github.com/rust-lang/mdBook.git
synced 2025-12-27 12:43:52 -05:00
This renames the "sections" list to "items". In practice, this list has contained more than just "sections" since parts were added. Also, the rest of the code consistently uses the term "items", since the values it contains are called `BookItem`s. Finally, the naming has always been a little confusing to me. This is a very disruptive change, and I'm not doing it lightly. However, since there are a number of other API changes going into 0.5, I think now is an ok time to change this.
165 lines
5.1 KiB
Rust
165 lines
5.1 KiB
Rust
//! A basic example of a preprocessor that does nothing.
|
|
|
|
use crate::nop_lib::Nop;
|
|
use clap::{Arg, ArgMatches, Command};
|
|
use mdbook_preprocessor::book::Book;
|
|
use mdbook_preprocessor::errors::Result;
|
|
use mdbook_preprocessor::{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<()> {
|
|
let (ctx, book) = mdbook_preprocessor::parse_input(io::stdin())?;
|
|
|
|
let book_version = Version::parse(&ctx.mdbook_version)?;
|
|
let version_req = VersionReq::parse(mdbook_preprocessor::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_preprocessor::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).unwrap();
|
|
|
|
// 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.
|
|
#[allow(unreachable_pub, reason = "wouldn't be a problem in a proper lib.rs")]
|
|
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> {
|
|
// In testing we want to tell the preprocessor to blow up by setting a
|
|
// particular config value
|
|
match ctx
|
|
.config
|
|
.get::<bool>("preprocessor.nop-preprocessor.blow-up")
|
|
{
|
|
Ok(Some(true)) => anyhow::bail!("Boom!!1!"),
|
|
Ok(_) => {}
|
|
Err(e) => anyhow::bail!("expect bool for blow-up: {e}"),
|
|
}
|
|
|
|
// we *are* a no-op preprocessor after all
|
|
Ok(book)
|
|
}
|
|
|
|
fn supports_renderer(&self, renderer: &str) -> Result<bool> {
|
|
Ok(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",
|
|
"src": "src",
|
|
"title": "TITLE"
|
|
},
|
|
"preprocessor": {
|
|
"nop": {}
|
|
}
|
|
},
|
|
"renderer": "html",
|
|
"mdbook_version": "0.4.21"
|
|
},
|
|
{
|
|
"items": [
|
|
{
|
|
"Chapter": {
|
|
"name": "Chapter 1",
|
|
"content": "# Chapter 1\n",
|
|
"number": [1],
|
|
"sub_items": [],
|
|
"path": "chapter_1.md",
|
|
"source_path": "chapter_1.md",
|
|
"parent_names": []
|
|
}
|
|
}
|
|
]
|
|
}
|
|
]"##;
|
|
let input_json = input_json.as_bytes();
|
|
|
|
let (ctx, book) = mdbook_preprocessor::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);
|
|
}
|
|
}
|
|
}
|