diff --git a/extensions/mdbook/test/plugins/BUILD.bazel b/extensions/mdbook/test/plugins/BUILD.bazel new file mode 100644 index 0000000000..82cf6572ee --- /dev/null +++ b/extensions/mdbook/test/plugins/BUILD.bazel @@ -0,0 +1,30 @@ +load("@bazel_skylib//rules:build_test.bzl", "build_test") +load("@rules_rust//rust:defs.bzl", "rust_binary", "rust_test") +load("//:defs.bzl", "mdbook") + +rust_binary( + name = "mdbook-test-preprocessor", + srcs = ["mdbook_test_preprocessor.rs"], + edition = "2021", +) + +mdbook( + name = "plugins", + srcs = glob(["src/**/*.md"]), + book = "book.toml", + plugins = [":mdbook-test-preprocessor"], +) + +rust_test( + name = "plugins_test", + srcs = ["plugins_test.rs"], + data = [":plugins"], + edition = "2021", + rustc_env = {"MDBOOK_PLUGINS_OUTPUT": "$(rlocationpath :plugins)"}, + deps = ["@rules_rust//rust/runfiles"], +) + +build_test( + name = "plugins_build_test", + targets = [":plugins"], +) diff --git a/extensions/mdbook/test/plugins/book.toml b/extensions/mdbook/test/plugins/book.toml new file mode 100644 index 0000000000..6e898ea4b5 --- /dev/null +++ b/extensions/mdbook/test/plugins/book.toml @@ -0,0 +1,8 @@ +[book] +authors = ["Plugin Test"] +language = "en" +src = "src" +title = "Plugin Test Book" + +[preprocessor.test-preprocessor] +command = "mdbook-test-preprocessor" diff --git a/extensions/mdbook/test/plugins/mdbook_test_preprocessor.rs b/extensions/mdbook/test/plugins/mdbook_test_preprocessor.rs new file mode 100644 index 0000000000..6e47470fd8 --- /dev/null +++ b/extensions/mdbook/test/plugins/mdbook_test_preprocessor.rs @@ -0,0 +1,42 @@ +use std::io::{self, Read}; + +fn main() { + let args: Vec = std::env::args().collect(); + if args.len() > 1 && args[1] == "supports" { + std::process::exit(0); + } + + let mut input = String::new(); + io::stdin().read_to_string(&mut input).unwrap(); + + let modified = input.replace("{{secret}}", "42"); + + // mdBook preprocessors receive a JSON array containing `[context, + // book]` on stdin. They are expected to return only the `book` + // object on stdout. + // + // This is a very simple JSON parser that counts brackets to find + // the comma separating the two elements, mapping `[context, + // book]` to `book`. + let mut depth = 0; + let mut split_index = 0; + let bytes = modified.as_bytes(); + for (i, &b) in bytes.iter().enumerate() { + if b == b'[' || b == b'{' { + depth += 1; + } else if b == b']' || b == b'}' { + depth -= 1; + } else if b == b',' && depth == 1 { + split_index = i; + break; + } + } + + if split_index == 0 { + eprintln!("Failed to parse mdbook input."); + std::process::exit(1); + } + + let book = &modified[split_index + 1..modified.len() - 1]; // Skip comma and last bracket + print!("{book}"); +} diff --git a/extensions/mdbook/test/plugins/plugins_test.rs b/extensions/mdbook/test/plugins/plugins_test.rs new file mode 100644 index 0000000000..1a52d90684 --- /dev/null +++ b/extensions/mdbook/test/plugins/plugins_test.rs @@ -0,0 +1,16 @@ +use runfiles::{rlocation, Runfiles}; +use std::fs; + +#[test] +fn test_preprocessor_output() { + let r = Runfiles::create().unwrap(); + + let dir = rlocation!(r, env!("MDBOOK_PLUGINS_OUTPUT")).unwrap(); + + let index_html = dir.join("index.html"); + let content = fs::read_to_string(index_html).unwrap(); + + // Check if the preprocessor successfully replaced {{secret}} with 42 + assert!(content.contains("The secret is: 42.")); + assert!(!content.contains("{{secret}}")); +} diff --git a/extensions/mdbook/test/plugins/src/SUMMARY.md b/extensions/mdbook/test/plugins/src/SUMMARY.md new file mode 100644 index 0000000000..13e9f0ea9a --- /dev/null +++ b/extensions/mdbook/test/plugins/src/SUMMARY.md @@ -0,0 +1,3 @@ +# Summary + +[Index](index.md) diff --git a/extensions/mdbook/test/plugins/src/index.md b/extensions/mdbook/test/plugins/src/index.md new file mode 100644 index 0000000000..c6883cf1d7 --- /dev/null +++ b/extensions/mdbook/test/plugins/src/index.md @@ -0,0 +1,3 @@ +# Plugin Test + +The secret is: {{secret}}.