use crate::nop_lib::NopPost; use clap::{Arg, ArgMatches, Command}; use mdxbook::errors::Error; use mdxbook::postprocess::{CmdPostprocessor, Postprocessor, Rendered}; use semver::{Version, VersionReq}; use std::io; use std::process; pub fn make_app() -> Command { Command::new("nop-postprocessor") .about("A mdxbook postprocessor which does precisely nothing") .subcommand( Command::new("supports") .arg(Arg::new("renderer").required(true)) .about("Check whether a renderer is supported by this postprocessor"), ) } fn main() { let matches = make_app().get_matches(); // Users will want to construct their own preprocessor here let preprocessor = NopPost::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 Postprocessor) -> Result<(), Error> { let (ctx, book) = CmdPostprocessor::parse_input(io::stdin())?; let book_version = Version::parse(&ctx.mdbook_version)?; let version_req = VersionReq::parse(mdxbook::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(), mdxbook::MDBOOK_VERSION, ctx.mdbook_version ); } let processed_book = match book { Rendered::Book(b) => serde_json::to_writer(io::stdout(), &pre.postprocess_book(&ctx, b)?)?, Rendered::Document(d) => { serde_json::to_writer(io::stdout(), &pre.postprocess_document(&ctx, d)?)? } }; Ok(processed_book) } fn handle_supports(pre: &dyn Postprocessor, sub_args: &ArgMatches) -> ! { let renderer = sub_args .get_one::("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 `NopPost` preprocessor. This would usually go /// in your main `lib.rs` file. mod nop_lib { use super::*; use mdxbook::postprocess::{ CmdPostprocessor, Postprocessor, PostprocessorContext, RenderedBook, RenderedDocumentOfBook, }; /// A no-op preprocessor. #[derive(Clone)] pub struct NopPost; impl NopPost { pub fn new() -> NopPost { NopPost } } impl Postprocessor for NopPost { fn name(&self) -> &str { "nop-postprocessor" } fn supports_renderer(&self, renderer: &str) -> bool { renderer != "not-supported" } fn postprocess_document( &self, context: &PostprocessorContext, rendered_document: RenderedDocumentOfBook, ) -> Result { // In testing we want to tell the postprocessor to blow up by setting a // particular config value if let Some(nop_cfg) = context.config.get_postprocessor(self.name()) { if nop_cfg.contains_key("blow-up") { anyhow::bail!("Boom!!1!"); } } // we *are* a no-op preprocessor after all Ok(rendered_document) } fn postprocess_book( &self, context: &PostprocessorContext, rendered_book: RenderedBook, ) -> Result { // In testing we want to tell the preprocessor to blow up by setting a // particular config value if let Some(nop_cfg) = context.config.get_postprocessor(self.name()) { if nop_cfg.contains_key("blow-up") { anyhow::bail!("Boom!!1!"); } } // we *are* a no-op preprocessor after all Ok(rendered_book) } fn clone_dyn(&self) -> Box { Box::new(self.clone()) } fn to_cmd_postprocessor(&self) -> Option { Some(CmdPostprocessor::new( self.name().to_string(), "cargo run --example nop-postprocessor --".to_string(), )) } } }