initial commit
This commit is contained in:
75
helpers/mdbook-exercise-linker/src/lib.rs
Normal file
75
helpers/mdbook-exercise-linker/src/lib.rs
Normal file
@@ -0,0 +1,75 @@
|
||||
use anyhow::{Context, Error};
|
||||
use mdbook::book::Book;
|
||||
use mdbook::preprocess::{Preprocessor, PreprocessorContext};
|
||||
use mdbook::BookItem;
|
||||
|
||||
pub struct ExerciseLinker;
|
||||
|
||||
impl ExerciseLinker {
|
||||
pub fn new() -> ExerciseLinker {
|
||||
ExerciseLinker
|
||||
}
|
||||
}
|
||||
|
||||
impl Preprocessor for ExerciseLinker {
|
||||
fn name(&self) -> &str {
|
||||
"exercise-linker"
|
||||
}
|
||||
|
||||
fn run(&self, ctx: &PreprocessorContext, mut book: Book) -> Result<Book, Error> {
|
||||
let config = ctx
|
||||
.config
|
||||
.get_preprocessor(self.name())
|
||||
.context("Failed to get preprocessor configuration")?;
|
||||
let key = String::from("exercise_root_url");
|
||||
let root_url = config
|
||||
.get(&key)
|
||||
.context("Failed to get `exercise_root_url`")?;
|
||||
let root_url = root_url
|
||||
.as_str()
|
||||
.context("`exercise_root_url` is not a string")?
|
||||
.to_owned();
|
||||
|
||||
book.sections
|
||||
.iter_mut()
|
||||
.for_each(|i| process_book_item(i, &ctx.renderer, &root_url));
|
||||
Ok(book)
|
||||
}
|
||||
|
||||
fn supports_renderer(&self, _renderer: &str) -> bool {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
fn process_book_item(item: &mut BookItem, renderer: &str, root_url: &str) {
|
||||
match item {
|
||||
BookItem::Chapter(chapter) => {
|
||||
chapter.sub_items.iter_mut().for_each(|item| {
|
||||
process_book_item(item, renderer, root_url);
|
||||
});
|
||||
|
||||
let Some(source_path) = &chapter.source_path else {
|
||||
return;
|
||||
};
|
||||
let source_path = source_path.display().to_string();
|
||||
|
||||
// Ignore non-exercise chapters
|
||||
if !source_path.chars().take(2).all(|c| c.is_digit(10)) {
|
||||
return;
|
||||
}
|
||||
|
||||
let exercise_path = source_path.strip_suffix(".md").unwrap();
|
||||
let link_section = format!(
|
||||
"\n## Exercise\n\nThe exercise for this section is located in [`{exercise_path}`]({})\n",
|
||||
format!("{}/{}", root_url, exercise_path)
|
||||
);
|
||||
chapter.content.push_str(&link_section);
|
||||
|
||||
if renderer == "pandoc" {
|
||||
chapter.content.push_str("`\\newpage`{=latex}\n");
|
||||
}
|
||||
}
|
||||
BookItem::Separator => {}
|
||||
BookItem::PartTitle(_) => {}
|
||||
}
|
||||
}
|
||||
67
helpers/mdbook-exercise-linker/src/main.rs
Normal file
67
helpers/mdbook-exercise-linker/src/main.rs
Normal file
@@ -0,0 +1,67 @@
|
||||
use std::io;
|
||||
use std::process;
|
||||
|
||||
use clap::{Arg, ArgMatches, Command};
|
||||
use mdbook::errors::Error;
|
||||
use mdbook::preprocess::{CmdPreprocessor, Preprocessor};
|
||||
use semver::{Version, VersionReq};
|
||||
|
||||
use mdbook_exercise_linker::ExerciseLinker;
|
||||
|
||||
pub fn make_app() -> Command {
|
||||
Command::new("exercise-linker").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 = ExerciseLinker::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);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user