From 767fb66c035f72a30cb99e1223630db6fa97abbf Mon Sep 17 00:00:00 2001 From: sgoudham Date: Fri, 29 Apr 2022 04:41:40 +0100 Subject: [PATCH] Create proper skeleton --- src/bin/mdbook-template.rs | 64 ++++++++++++++++++++++ src/lib.rs | 106 ++++++++++++++++++++++++++++++++++--- 2 files changed, 164 insertions(+), 6 deletions(-) create mode 100644 src/bin/mdbook-template.rs diff --git a/src/bin/mdbook-template.rs b/src/bin/mdbook-template.rs new file mode 100644 index 0000000..48314ac --- /dev/null +++ b/src/bin/mdbook-template.rs @@ -0,0 +1,64 @@ +use std::{io, process}; + +use clap::{Arg, ArgMatches, Command}; +use mdbook::errors::Error; +use mdbook::preprocess::{CmdPreprocessor, Preprocessor}; +use semver::{Version, VersionReq}; + +use mdbook_template::Template; + +fn main() { + let matches = make_app().get_matches(); + + let preprocessor = Template::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); + } +} + +pub fn make_app() -> Command<'static> { + Command::new("mdbook-template") + .about("A mdbook preprocessor that allows the re-usability of template files with variable arguments") + .subcommand( + Command::new("supports") + .arg(Arg::new("renderer").required(true)) + .about("Check whether a renderer is supported by this preprocessor"), + ) +} + +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.value_of("renderer").expect("Required argument"); + let supported = pre.supports_renderer(renderer); + + if supported { + process::exit(0); + } else { + process::exit(1); + } +} \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs index 1b4a90c..bfd1f89 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,8 +1,102 @@ -#[cfg(test)] -mod tests { - #[test] - fn it_works() { - let result = 2 + 2; - assert_eq!(result, 4); +use fancy_regex::{CaptureMatches, Captures, Regex}; +use lazy_static::lazy_static; +use log::info; +use mdbook::book::Book; +use mdbook::errors::Result; +use mdbook::preprocess::{Preprocessor, PreprocessorContext}; +use mdbook::BookItem; +use toml::Value; + +lazy_static! { + static ref ARGS: Regex = Regex::new(r"(?<=\s|\A)([^\s=]+)=(.*?)(?=(?:\s[^\s=]+=|$))").unwrap(); + static ref WHOLE_TEMPLATE: Regex = Regex::new(r"\\\{\{\#.*\}\}|\{\{\s*\#([a-zA-Z0-9_]+)\s+([a-zA-Z0-9_.-/]+)\s*([^}]+)\}\}").unwrap(); + // static ref RE: Regex = Regex::new(r"\\\{\{\#.*\}\}|\{\{\s*\#([a-zA-Z0-9_]+)\s+([a-zA-Z0-9-_.]+)\s*((?<=\s|\A)([^\s=]+)=(.*?)(?=(?:\s[^\s=]+=|$)))*\}\}").unwrap(); +} + +#[derive(Default)] +pub struct Template; + +impl Template { + pub fn new() -> Self { + Template + } +} + +impl Preprocessor for Template { + fn name(&self) -> &str { + "template" } + + fn run(&self, ctx: &PreprocessorContext, mut book: Book) -> Result { + /* + TODO - 29/04/2022 + Store All Files in Key/Value Pairs + Start iterating through each chapter in the book + 1. Get template string + 2. Remove }} at the end of template string + 3. Store template string arguments in Key/Value Pairs + 4. Get the template and dynamically find/replace + 5. Set the chapter content to the new one + Return the book to mdbook + */ + + env_logger::init_from_env(env_logger::Env::default().default_filter_or("info")); + + if let Some(config) = ctx.config.get_preprocessor(self.name()) { + let default = Value::String(String::from("templates/")); + let template_dir = config.get("templates-dir").unwrap_or(&default); + info!("Reading from directory {}", template_dir); + } + + book.for_each_mut(|book_item| { + if let BookItem::Chapter(ref mut chapter) = book_item { + chapter.content = String::from("All content is now replaced"); + } + }); + + Ok(book) + } + + fn supports_renderer(&self, renderer: &str) -> bool { + renderer == "html" + } +} + +#[derive(PartialEq, Debug, Clone)] +pub struct Link<'a> { + start_index: usize, + end_index: usize, + pub link_text: &'a str, +} + +impl<'a> Link<'a> { + fn from_capture(cap: Captures<'a>) -> Option> { + cap.get(0).map(|mat| Link { + start_index: mat.start(), + end_index: mat.end(), + link_text: mat.as_str(), + }) + } +} + +pub struct LinkIter<'a>(CaptureMatches<'a, 'a>); + +impl<'a> Iterator for LinkIter<'a> { + type Item = Link<'a>; + fn next(&mut self) -> Option> { + for cap in &mut self.0 { + if let Some(inc) = Link::from_capture(cap.unwrap()) { + return Some(inc); + } + } + None + } +} + +pub fn extract_template_links(contents: &str) -> LinkIter<'_> { + LinkIter(WHOLE_TEMPLATE.captures_iter(contents)) } + +pub fn extract_template_arguments(contents: &str) -> LinkIter<'_> { + LinkIter(ARGS.captures_iter(contents)) +} \ No newline at end of file