diff --git a/.github/workflows/install-mdbook/action.yml b/.github/workflows/install-mdbook/action.yml index e7e93dd3..9d1f62fc 100644 --- a/.github/workflows/install-mdbook/action.yml +++ b/.github/workflows/install-mdbook/action.yml @@ -22,6 +22,10 @@ runs: run: cargo install mdbook-i18n-helpers --locked --version 0.2.3 shell: bash - - name: Install exerciser + - name: Install mdbook-exerciser run: cargo install --path mdbook-exerciser --locked shell: bash + + - name: Install mdbook-course + run: cargo install --path mdbook-course --locked + shell: bash diff --git a/Cargo.lock b/Cargo.lock index aede60bb..9571f98e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -257,18 +257,18 @@ dependencies = [ [[package]] name = "clap" -version = "4.4.2" +version = "4.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a13b88d2c62ff462f88e4a121f17a82c1af05693a2f192b5c38d14de73c19f6" +checksum = "b1d7b8d5ec32af0fadc644bf1fd509a688c2103b185644bb1e29d164e0703136" dependencies = [ "clap_builder", ] [[package]] name = "clap_builder" -version = "4.4.2" +version = "4.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2bb9faaa7c2ef94b2743a21f5a29e6f0010dff4caa69ac8e9d6cf8b6fa74da08" +checksum = "5179bb514e4d7c2051749d8fcefa2ed6d06a9f4e6d69faf3805f5d80b8cf8d56" dependencies = [ "anstream", "anstyle", @@ -963,17 +963,6 @@ dependencies = [ "libc", ] -[[package]] -name = "io-lifetimes" -version = "1.0.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2" -dependencies = [ - "hermit-abi 0.3.2", - "libc", - "windows-sys", -] - [[package]] name = "ipnet" version = "2.8.0" @@ -987,7 +976,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cb0889898416213fab133e1d33a0e5858a48177452750691bde3666d0fdbaf8b" dependencies = [ "hermit-abi 0.3.2", - "rustix 0.38.13", + "rustix", "windows-sys", ] @@ -1038,12 +1027,6 @@ version = "0.2.147" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" -[[package]] -name = "linux-raw-sys" -version = "0.3.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519" - [[package]] name = "linux-raw-sys" version = "0.4.7" @@ -1098,6 +1081,16 @@ version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2532096657941c2fea9c289d370a250971c689d4f143798ff67113ec042024a5" +[[package]] +name = "matter" +version = "0.1.0-alpha4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc16e839c57e0ad77957c42d39baab3692a1c6fa47692066470cddc24a5b0cd0" +dependencies = [ + "lazy_static", + "regex", +] + [[package]] name = "mdbook" version = "0.4.34" @@ -1132,6 +1125,19 @@ dependencies = [ "warp", ] +[[package]] +name = "mdbook-course" +version = "0.1.0" +dependencies = [ + "anyhow", + "clap", + "log", + "matter", + "mdbook", + "pretty_env_logger", + "serde_json", +] + [[package]] name = "mdbook-exerciser" version = "0.1.0" @@ -1781,20 +1787,6 @@ dependencies = [ "semver", ] -[[package]] -name = "rustix" -version = "0.37.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d69718bf81c6127a49dc64e44a742e8bb9213c0ff8869a22c308f84c1d4ab06" -dependencies = [ - "bitflags 1.3.2", - "errno", - "io-lifetimes", - "libc", - "linux-raw-sys 0.3.8", - "windows-sys", -] - [[package]] name = "rustix" version = "0.38.13" @@ -1804,7 +1796,7 @@ dependencies = [ "bitflags 2.4.0", "errno", "libc", - "linux-raw-sys 0.4.7", + "linux-raw-sys", "windows-sys", ] @@ -1938,9 +1930,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.106" +version = "1.0.107" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2cc66a619ed80bf7a0f6b17dd063a84b88f6dea1813737cf469aef1d081142c2" +checksum = "6b420ce6e3d8bd882e9b243c6eed35dbc9a6110c9769e74b584e0d68d1f20c65" dependencies = [ "itoa", "ryu", @@ -2131,7 +2123,7 @@ dependencies = [ "cfg-if", "fastrand", "redox_syscall", - "rustix 0.38.13", + "rustix", "windows-sys", ] @@ -2157,11 +2149,11 @@ dependencies = [ [[package]] name = "terminal_size" -version = "0.2.6" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e6bf6f19e9f8ed8d4048dc22981458ebcf406d67e94cd422e5ecd73d63b3237" +checksum = "21bebf2b7c9e0a515f6e0f8c51dc0f8e4696391e6f1ff30379559f8365fb0df7" dependencies = [ - "rustix 0.37.23", + "rustix", "windows-sys", ] diff --git a/Cargo.toml b/Cargo.toml index 7f1b06da..0c36b41e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,7 @@ [workspace] members = [ "mdbook-exerciser", + "mdbook-course", "src/exercises", "src/bare-metal/useful-crates/allocator-example", "src/bare-metal/useful-crates/zerocopy-example", diff --git a/README.md b/README.md index b5ced429..54617a24 100644 --- a/README.md +++ b/README.md @@ -36,6 +36,7 @@ The course is built using a few tools: - [mdbook-svgbob](https://github.com/boozook/mdbook-svgbob) - [mdbook-i18n-helpers](https://github.com/google/mdbook-i18n-helpers) - [mdbook-exerciser](mdbook-exerciser/) +- [mdbook-course](mdbook-course/) First clone the repository: @@ -51,6 +52,7 @@ cargo install mdbook cargo install mdbook-svgbob cargo install mdbook-i18n-helpers cargo install --path mdbook-exerciser +cargo install --path mdbook-course ``` Run diff --git a/book.toml b/book.toml index 92d09724..a563aa30 100644 --- a/book.toml +++ b/book.toml @@ -19,6 +19,7 @@ renderers = ["html"] after = ["gettext"] class = "bob" +[preprocessor.course] # Enable this preprocessor to overlay a large red rectangle on the # pages. This will show you an estimate of what the course # participants can see during the presentation. @@ -29,7 +30,12 @@ class = "bob" [output.html] curly-quotes = true additional-js = ["speaker-notes.js"] -additional-css = ["svgbob.css", "speaker-notes.css", "language-picker.css"] +additional-css = [ + "svgbob.css", + "speaker-notes.css", + "language-picker.css", + "frontmatter.css", +] site-url = "/comprehensive-rust/" git-repository-url = "https://github.com/google/comprehensive-rust" edit-url-template = "https://github.com/google/comprehensive-rust/edit/main/{path}" diff --git a/frontmatter.css b/frontmatter.css new file mode 100644 index 00000000..ab367ad4 --- /dev/null +++ b/frontmatter.css @@ -0,0 +1,7 @@ +pre.frontmatter { + float: right; + font-size: 80%; + width: 40%; + background: var(--sidebar-bg); + padding: 0.25em; +} diff --git a/mdbook-course/Cargo.toml b/mdbook-course/Cargo.toml new file mode 100644 index 00000000..dd3b9e48 --- /dev/null +++ b/mdbook-course/Cargo.toml @@ -0,0 +1,18 @@ +[package] +name = "mdbook-course" +version = "0.1.0" +authors = ["Dustin Mitchell "] +edition = "2021" +license = "Apache-2.0" +publish = false +repository = "https://github.com/google/comprehensive-rust" +description = "An mdbook preprocessor for comprehensive-rust." + +[dependencies] +anyhow = "1.0.68" +clap = "4.4.4" +log = "0.4.17" +matter = "0.1.0-alpha4" +mdbook = "0.4.25" +pretty_env_logger = "0.4.0" +serde_json = "1.0.107" diff --git a/mdbook-course/README.md b/mdbook-course/README.md new file mode 100644 index 00000000..c349630c --- /dev/null +++ b/mdbook-course/README.md @@ -0,0 +1,17 @@ +# mdbook-course + +This is an mdBook preprocessor to handle some specific details of Comprehensive +Rust. + +## Frontmatter + +The preprocessor parses "frontmatter" -- YAML between `---` at the beginning of +a Markdown file -- and removes it from the rendered result. At the moment, to +aid review of the new course, it places this content in a `
` block.
+
+## Future Work
+
+- Parse the `minutes` property from frontmatter and
+  - Generate a course timeline
+  - Include timing information in the speaker notes
+- Generate per-segment tables of contents.
diff --git a/mdbook-course/src/frontmatter.rs b/mdbook-course/src/frontmatter.rs
new file mode 100644
index 00000000..842cdfb7
--- /dev/null
+++ b/mdbook-course/src/frontmatter.rs
@@ -0,0 +1,41 @@
+// Copyright 2023 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+use matter::matter;
+use mdbook::book::{Book, BookItem};
+use mdbook::preprocess::PreprocessorContext;
+
+pub fn remove_frontmatter(
+    ctx: &PreprocessorContext,
+    book: &mut Book,
+) -> anyhow::Result<()> {
+    let is_html = ctx.renderer == "html";
+    book.for_each_mut(|chapter| {
+        let BookItem::Chapter(chapter) = chapter else {
+            return;
+        };
+        if let Some((frontmatter, content)) = matter(&chapter.content) {
+            if is_html {
+                // For the moment, include the frontmatter in the slide in a floating 
, for review
+                // purposes.
+                let pre = format!(r#"
{frontmatter}
"#); + chapter.content = format!("{pre}\n\n{content}"); + } else { + // For non-HTML renderers, just strip the frontmatter. + chapter.content = content; + } + } + }); + Ok(()) +} diff --git a/mdbook-course/src/lib.rs b/mdbook-course/src/lib.rs new file mode 100644 index 00000000..f9175571 --- /dev/null +++ b/mdbook-course/src/lib.rs @@ -0,0 +1,15 @@ +// Copyright 2023 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +pub mod frontmatter; diff --git a/mdbook-course/src/main.rs b/mdbook-course/src/main.rs new file mode 100644 index 00000000..1da819e0 --- /dev/null +++ b/mdbook-course/src/main.rs @@ -0,0 +1,46 @@ +// Copyright 2023 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use clap::{Arg, Command}; +use mdbook::preprocess::CmdPreprocessor; +use mdbook_course::frontmatter::remove_frontmatter; +use std::io::{stdin, stdout}; +use std::process; + +fn main() { + pretty_env_logger::init(); + let app = Command::new("mdbook-course") + .about("mdbook preprocessor for Comprehensive Rust") + .subcommand(Command::new("supports").arg(Arg::new("renderer").required(true))); + let matches = app.get_matches(); + + if let Some(_) = matches.subcommand_matches("supports") { + // Support all renderers. + process::exit(0); + } + + if let Err(e) = preprocess() { + eprintln!("{}", e); + process::exit(1); + } +} + +fn preprocess() -> anyhow::Result<()> { + let (ctx, mut book) = CmdPreprocessor::parse_input(stdin())?; + + remove_frontmatter(&ctx, &mut book)?; + + serde_json::to_writer(stdout(), &book)?; + Ok(()) +}