From e45367494d2c6bd3b0dd006090b6e44b44c75b2e Mon Sep 17 00:00:00 2001 From: Eric Huss Date: Thu, 22 Feb 2024 15:10:48 -0800 Subject: [PATCH 01/21] Add mdbook-spec. --- mdbook-spec/.gitignore | 1 + mdbook-spec/Cargo.lock | 982 ++++++++++++++++++++++++++++++++++++++++ mdbook-spec/Cargo.toml | 14 + mdbook-spec/README.md | 3 + mdbook-spec/src/main.rs | 186 ++++++++ 5 files changed, 1186 insertions(+) create mode 100644 mdbook-spec/.gitignore create mode 100644 mdbook-spec/Cargo.lock create mode 100644 mdbook-spec/Cargo.toml create mode 100644 mdbook-spec/README.md create mode 100644 mdbook-spec/src/main.rs diff --git a/mdbook-spec/.gitignore b/mdbook-spec/.gitignore new file mode 100644 index 000000000..ea8c4bf7f --- /dev/null +++ b/mdbook-spec/.gitignore @@ -0,0 +1 @@ +/target diff --git a/mdbook-spec/Cargo.lock b/mdbook-spec/Cargo.lock new file mode 100644 index 000000000..a4da56e30 --- /dev/null +++ b/mdbook-spec/Cargo.lock @@ -0,0 +1,982 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "aho-corasick" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0" +dependencies = [ + "memchr", +] + +[[package]] +name = "android-tzdata" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + +[[package]] +name = "anstream" +version = "0.6.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e2e1ebcb11de5c03c67de28a7df593d32191b44939c482e97702baaaa6ab6a5" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2faccea4cc4ab4a667ce676a30e8ec13922a692c99bb8f5b11f1502c72e04220" + +[[package]] +name = "anstyle-parse" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c75ac65da39e5fe5ab759307499ddad880d724eed2f6ce5b5e8a26f4f387928c" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e28923312444cdd728e4738b3f9c9cac739500909bb3d3c94b43551b16517648" +dependencies = [ + "windows-sys 0.52.0", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1cd54b81ec8d6180e24654d0b371ad22fc3dd083b6ff8ba325b72e00c87660a7" +dependencies = [ + "anstyle", + "windows-sys 0.52.0", +] + +[[package]] +name = "anyhow" +version = "1.0.79" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "080e9890a082662b09c1ad45f567faeeb47f22b5fb23895fbe1e651e718e25ca" + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitflags" +version = "2.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed570934406eb16438a4e976b1b4500774099c13b8cb96eec99f620f05090ddf" + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "bstr" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c48f0051a4b4c5e0b6d365cd04af53aeaa209e3cc15ec2cdb69e73cc87fbd0dc" +dependencies = [ + "memchr", + "regex-automata", + "serde", +] + +[[package]] +name = "bumpalo" +version = "3.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec" + +[[package]] +name = "cc" +version = "1.0.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" +dependencies = [ + "libc", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "chrono" +version = "0.4.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f13690e35a5e4ace198e7beea2895d29f3a9cc55015fcebe6336bd2010af9eb" +dependencies = [ + "android-tzdata", + "iana-time-zone", + "num-traits", + "windows-targets 0.52.0", +] + +[[package]] +name = "clap" +version = "4.4.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e578d6ec4194633722ccf9544794b71b1385c3c027efe0c55db226fc880865c" +dependencies = [ + "clap_builder", +] + +[[package]] +name = "clap_builder" +version = "4.4.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4df4df40ec50c46000231c914968278b1eb05098cf8f1b3a518a95030e71d1c7" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", + "strsim", + "terminal_size", +] + +[[package]] +name = "clap_complete" +version = "4.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df631ae429f6613fcd3a7c1adbdb65f637271e561b03680adaa6573015dfb106" +dependencies = [ + "clap", +] + +[[package]] +name = "clap_lex" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "702fc72eb24e5a1e48ce58027a675bc24edd52096d5397d4aea7c6dd9eca0bd1" + +[[package]] +name = "colorchoice" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" + +[[package]] +name = "core-foundation-sys" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" + +[[package]] +name = "cpufeatures" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504" +dependencies = [ + "libc", +] + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "crypto-common", +] + +[[package]] +name = "env_logger" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cd405aab171cb85d6735e5c8d9db038c17d3ca007a4d2c25f337935c3d90580" +dependencies = [ + "humantime", + "is-terminal", + "log", + "regex", + "termcolor", +] + +[[package]] +name = "errno" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "fastrand" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5" + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "handlebars" +version = "4.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "faa67bab9ff362228eb3d00bd024a4965d8231bbb7921167f0cfa66c6626b225" +dependencies = [ + "log", + "pest", + "pest_derive", + "serde", + "serde_json", + "thiserror", +] + +[[package]] +name = "hermit-abi" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d3d0e0f38255e7fa3cf31335b3a56f05febd18025f4db5ef7a0cfb4f8da651f" + +[[package]] +name = "humantime" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" + +[[package]] +name = "iana-time-zone" +version = "0.1.59" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6a67363e2aa4443928ce15e57ebae94fd8949958fd1223c4cfc0cd473ad7539" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "wasm-bindgen", + "windows-core", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + +[[package]] +name = "is-terminal" +version = "0.4.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bad00257d07be169d870ab665980b06cdb366d792ad690bf2e76876dc503455" +dependencies = [ + "hermit-abi", + "rustix", + "windows-sys 0.52.0", +] + +[[package]] +name = "itoa" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c" + +[[package]] +name = "js-sys" +version = "0.3.67" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a1d36f1235bc969acba30b7f5990b864423a6068a10f7c90ae8f0112e3a59d1" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "libc" +version = "0.2.153" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" + +[[package]] +name = "linux-raw-sys" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c" + +[[package]] +name = "log" +version = "0.4.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" + +[[package]] +name = "mdbook" +version = "0.4.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80992cb0e05f22cc052c99f8e883f1593b891014b96a8b4637fd274d7030c85e" +dependencies = [ + "anyhow", + "chrono", + "clap", + "clap_complete", + "env_logger", + "handlebars", + "log", + "memchr", + "once_cell", + "opener", + "pathdiff", + "pulldown-cmark", + "regex", + "serde", + "serde_json", + "shlex", + "tempfile", + "toml", + "topological-sort", +] + +[[package]] +name = "mdbook-spec" +version = "0.0.0" +dependencies = [ + "anyhow", + "mdbook", + "pathdiff", + "regex", + "semver", + "serde_json", +] + +[[package]] +name = "memchr" +version = "2.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149" + +[[package]] +name = "normpath" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec60c60a693226186f5d6edf073232bfb6464ed97eb22cf3b01c1e8198fd97f5" +dependencies = [ + "windows-sys 0.48.0", +] + +[[package]] +name = "num-traits" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c" +dependencies = [ + "autocfg", +] + +[[package]] +name = "once_cell" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" + +[[package]] +name = "opener" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c62dcb6174f9cb326eac248f07e955d5d559c272730b6c03e396b443b562788" +dependencies = [ + "bstr", + "normpath", + "winapi", +] + +[[package]] +name = "pathdiff" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8835116a5c179084a830efb3adc117ab007512b535bc1a21c991d3b32a6b44dd" + +[[package]] +name = "pest" +version = "2.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f200d8d83c44a45b21764d1916299752ca035d15ecd46faca3e9a2a2bf6ad06" +dependencies = [ + "memchr", + "thiserror", + "ucd-trie", +] + +[[package]] +name = "pest_derive" +version = "2.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bcd6ab1236bbdb3a49027e920e693192ebfe8913f6d60e294de57463a493cfde" +dependencies = [ + "pest", + "pest_generator", +] + +[[package]] +name = "pest_generator" +version = "2.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a31940305ffc96863a735bef7c7994a00b325a7138fdbc5bda0f1a0476d3275" +dependencies = [ + "pest", + "pest_meta", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "pest_meta" +version = "2.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7ff62f5259e53b78d1af898941cdcdccfae7385cf7d793a6e55de5d05bb4b7d" +dependencies = [ + "once_cell", + "pest", + "sha2", +] + +[[package]] +name = "proc-macro2" +version = "1.0.78" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "pulldown-cmark" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57206b407293d2bcd3af849ce869d52068623f19e1b5ff8e8778e3309439682b" +dependencies = [ + "bitflags 2.4.2", + "memchr", + "unicase", +] + +[[package]] +name = "quote" +version = "1.0.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "redox_syscall" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" +dependencies = [ + "bitflags 1.3.2", +] + +[[package]] +name = "regex" +version = "1.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b62dbe01f0b06f9d8dc7d49e05a0785f153b00b2c227856282f671e0318c9b15" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5bb987efffd3c6d0d8f5f89510bb458559eab11e4f869acb20bf845e016259cd" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" + +[[package]] +name = "rustix" +version = "0.38.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ea3e1a662af26cd7a3ba09c0297a31af215563ecf42817c98df621387f4e949" +dependencies = [ + "bitflags 2.4.2", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.52.0", +] + +[[package]] +name = "ryu" +version = "1.0.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f98d2aa92eebf49b69786be48e4477826b256916e84a57ff2a4f21923b48eb4c" + +[[package]] +name = "semver" +version = "1.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b97ed7a9823b74f99c7742f5336af7be5ecd3eeafcb1507d1fa93347b1d589b0" + +[[package]] +name = "serde" +version = "1.0.196" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "870026e60fa08c69f064aa766c10f10b1d62db9ccd4d0abb206472bee0ce3b32" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.196" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33c85360c95e7d137454dc81d9a4ed2b8efd8fbe19cee57357b32b9771fccb67" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.113" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69801b70b1c3dac963ecb03a364ba0ceda9cf60c71cfe475e99864759c8b8a79" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "sha2" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "strsim" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" + +[[package]] +name = "syn" +version = "2.0.48" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f3531638e407dfc0814761abb7c00a5b54992b849452a0646b7f65c9f770f3f" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "tempfile" +version = "3.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01ce4141aa927a6d1bd34a041795abd0db1cccba5d5f24b009f694bdf3a1f3fa" +dependencies = [ + "cfg-if", + "fastrand", + "redox_syscall", + "rustix", + "windows-sys 0.52.0", +] + +[[package]] +name = "termcolor" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "terminal_size" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21bebf2b7c9e0a515f6e0f8c51dc0f8e4696391e6f1ff30379559f8365fb0df7" +dependencies = [ + "rustix", + "windows-sys 0.48.0", +] + +[[package]] +name = "thiserror" +version = "1.0.56" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d54378c645627613241d077a3a79db965db602882668f9136ac42af9ecb730ad" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.56" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa0faa943b50f3db30a20aa7e265dbc66076993efed8463e8de414e5d06d3471" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "toml" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234" +dependencies = [ + "serde", +] + +[[package]] +name = "topological-sort" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea68304e134ecd095ac6c3574494fc62b909f416c4fca77e440530221e549d3d" + +[[package]] +name = "typenum" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" + +[[package]] +name = "ucd-trie" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed646292ffc8188ef8ea4d1e0e0150fb15a5c2e12ad9b8fc191ae7a8a7f3c4b9" + +[[package]] +name = "unicase" +version = "2.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7d2d4dafb69621809a81864c9c1b864479e1235c0dd4e199924b9742439ed89" +dependencies = [ + "version_check", +] + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + +[[package]] +name = "utf8parse" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "wasm-bindgen" +version = "0.2.90" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1223296a201415c7fad14792dbefaace9bd52b62d33453ade1c5b5f07555406" +dependencies = [ + "cfg-if", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.90" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcdc935b63408d58a32f8cc9738a0bffd8f05cc7c002086c6ef20b7312ad9dcd" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.90" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e4c238561b2d428924c49815533a8b9121c664599558a5d9ec51f8a1740a999" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.90" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bae1abb6806dc1ad9e560ed242107c0f6c84335f1749dd4e8ddb012ebd5e25a7" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.90" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d91413b1c31d7539ba5ef2451af3f0b833a005eb27a631cec32bc0635a8602b" + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596" +dependencies = [ + "winapi", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-core" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" +dependencies = [ + "windows-targets 0.52.0", +] + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets 0.48.5", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets 0.52.0", +] + +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", +] + +[[package]] +name = "windows-targets" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd" +dependencies = [ + "windows_aarch64_gnullvm 0.52.0", + "windows_aarch64_msvc 0.52.0", + "windows_i686_gnu 0.52.0", + "windows_i686_msvc 0.52.0", + "windows_x86_64_gnu 0.52.0", + "windows_x86_64_gnullvm 0.52.0", + "windows_x86_64_msvc 0.52.0", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" diff --git a/mdbook-spec/Cargo.toml b/mdbook-spec/Cargo.toml new file mode 100644 index 000000000..8832156cd --- /dev/null +++ b/mdbook-spec/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "mdbook-spec" +edition = "2021" +license = "MIT OR Apache-2.0" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +anyhow = "1.0.79" +mdbook = { version = "0.4.36", default-features = false } +pathdiff = "0.2.1" +regex = "1.10.3" +semver = "1.0.21" +serde_json = "1.0.113" diff --git a/mdbook-spec/README.md b/mdbook-spec/README.md new file mode 100644 index 000000000..8c293fa20 --- /dev/null +++ b/mdbook-spec/README.md @@ -0,0 +1,3 @@ +# mdbook-spec + +This is an mdbook preprocessor to add some extensions for the Rust specification. diff --git a/mdbook-spec/src/main.rs b/mdbook-spec/src/main.rs new file mode 100644 index 000000000..1fd8e4e03 --- /dev/null +++ b/mdbook-spec/src/main.rs @@ -0,0 +1,186 @@ +use mdbook::book::{Book, Chapter}; +use mdbook::errors::Error; +use mdbook::preprocess::{CmdPreprocessor, Preprocessor, PreprocessorContext}; +use mdbook::BookItem; +use regex::{Captures, Regex}; +use semver::{Version, VersionReq}; +use std::collections::BTreeMap; +use std::io; +use std::path::PathBuf; +use std::process; + +fn main() { + let mut args = std::env::args().skip(1); + match args.next().as_deref() { + Some("supports") => { + // Supports all renderers. + return; + } + Some(arg) => { + eprintln!("unknown argument: {arg}"); + std::process::exit(1); + } + None => {} + } + + let preprocessor = Spec::new(); + + 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(()) +} + +struct Spec { + deny_warnings: bool, + rule_re: Regex, + admonition_re: Regex, +} + +impl Spec { + pub fn new() -> Spec { + Spec { + deny_warnings: std::env::var("SPEC_DENY_WARNINGS").as_deref() == Ok("1"), + rule_re: Regex::new(r"(?m)^r\[([^]]+)]$").unwrap(), + admonition_re: Regex::new( + r"(?m)^ *> \[!(?[^]]+)\]\n(?
(?: *> .*\n)+)", + ) + .unwrap(), + } + } + + /// Converts lines that start with `r[…]` into a "rule" which has special + /// styling and can be linked to. + fn rule_definitions( + &self, + chapter: &Chapter, + found_rules: &mut BTreeMap, + ) -> String { + let source_path = chapter.source_path.clone().unwrap_or_default(); + let path = chapter.path.clone().unwrap_or_default(); + self.rule_re + .replace_all(&chapter.content, |caps: &Captures| { + let rule_id = &caps[1]; + if let Some((old, _)) = + found_rules.insert(rule_id.to_string(), (source_path.clone(), path.clone())) + { + let message = format!( + "rule `{rule_id}` defined multiple times\n\ + First location: {old:?}\n\ + Second location: {source_path:?}" + ); + if self.deny_warnings { + panic!("error: {message}"); + } else { + eprintln!("warning: {message}"); + } + } + format!( + "
\ + [{rule_id}]\ +
\n" + ) + }) + .to_string() + } + + /// Generates link references to all rules on all pages, so you can easily + /// refer to rules anywhere in the book. + fn auto_link_references( + &self, + chapter: &Chapter, + found_rules: &BTreeMap, + ) -> String { + let current_path = chapter.path.as_ref().unwrap().parent().unwrap(); + let definitions: String = found_rules + .iter() + .map(|(rule_id, (_, path))| { + let relative = pathdiff::diff_paths(path, current_path).unwrap(); + format!("[{rule_id}]: {}#{rule_id}\n", relative.display()) + }) + .collect(); + format!( + "{}\n\ + {definitions}", + chapter.content + ) + } + + /// Converts blockquotes with special headers into admonitions. + /// + /// The blockquote should look something like: + /// + /// ``` + /// > [!WARNING] + /// > ... + /// ``` + /// + /// This will add a `
` around the blockquote so that + /// it can be styled differently. Any text between the brackets that can + /// be a CSS class is valid. The actual styling needs to be added in a CSS + /// file. + fn admonitions(&self, chapter: &Chapter) -> String { + self.admonition_re + .replace_all(&chapter.content, |caps: &Captures| { + let lower = caps["admon"].to_lowercase(); + format!( + "
\n\n{}\n\n
\n", + &caps["blockquote"] + ) + }) + .to_string() + } +} + +impl Preprocessor for Spec { + fn name(&self) -> &str { + "nop-preprocessor" + } + + fn run(&self, _ctx: &PreprocessorContext, mut book: Book) -> Result { + let mut found_rules = BTreeMap::new(); + for section in &mut book.sections { + let BookItem::Chapter(ch) = section else { + continue; + }; + if ch.is_draft_chapter() { + continue; + } + ch.content = self.rule_definitions(&ch, &mut found_rules); + ch.content = self.admonitions(&ch); + } + for section in &mut book.sections { + let BookItem::Chapter(ch) = section else { + continue; + }; + if ch.is_draft_chapter() { + continue; + } + ch.content = self.auto_link_references(&ch, &found_rules); + } + + Ok(book) + } +} From 5fd14c99837b573df3ab29bc7337998fd6673b7b Mon Sep 17 00:00:00 2001 From: Eric Huss Date: Fri, 8 Mar 2024 13:37:04 -0800 Subject: [PATCH 02/21] Support automatic links to the standard library. --- mdbook-spec/Cargo.lock | 25 ++----- mdbook-spec/Cargo.toml | 1 + mdbook-spec/src/main.rs | 144 +++++++++++++++++++++++++++++++++++++++- 3 files changed, 148 insertions(+), 22 deletions(-) diff --git a/mdbook-spec/Cargo.lock b/mdbook-spec/Cargo.lock index a4da56e30..d77a44025 100644 --- a/mdbook-spec/Cargo.lock +++ b/mdbook-spec/Cargo.lock @@ -86,12 +86,6 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" -[[package]] -name = "bitflags" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" - [[package]] name = "bitflags" version = "2.4.2" @@ -398,6 +392,7 @@ dependencies = [ "regex", "semver", "serde_json", + "tempfile", ] [[package]] @@ -507,7 +502,7 @@ version = "0.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "57206b407293d2bcd3af849ce869d52068623f19e1b5ff8e8778e3309439682b" dependencies = [ - "bitflags 2.4.2", + "bitflags", "memchr", "unicase", ] @@ -521,15 +516,6 @@ dependencies = [ "proc-macro2", ] -[[package]] -name = "redox_syscall" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" -dependencies = [ - "bitflags 1.3.2", -] - [[package]] name = "regex" version = "1.10.3" @@ -565,7 +551,7 @@ version = "0.38.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6ea3e1a662af26cd7a3ba09c0297a31af215563ecf42817c98df621387f4e949" dependencies = [ - "bitflags 2.4.2", + "bitflags", "errno", "libc", "linux-raw-sys", @@ -651,13 +637,12 @@ dependencies = [ [[package]] name = "tempfile" -version = "3.9.0" +version = "3.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01ce4141aa927a6d1bd34a041795abd0db1cccba5d5f24b009f694bdf3a1f3fa" +checksum = "85b77fafb263dd9d05cbeac119526425676db3784113aa9295c88498cbf8bff1" dependencies = [ "cfg-if", "fastrand", - "redox_syscall", "rustix", "windows-sys 0.52.0", ] diff --git a/mdbook-spec/Cargo.toml b/mdbook-spec/Cargo.toml index 8832156cd..bbfd90342 100644 --- a/mdbook-spec/Cargo.toml +++ b/mdbook-spec/Cargo.toml @@ -12,3 +12,4 @@ pathdiff = "0.2.1" regex = "1.10.3" semver = "1.0.21" serde_json = "1.0.113" +tempfile = "3.10.1" diff --git a/mdbook-spec/src/main.rs b/mdbook-spec/src/main.rs index 1fd8e4e03..7e617f064 100644 --- a/mdbook-spec/src/main.rs +++ b/mdbook-spec/src/main.rs @@ -5,9 +5,11 @@ use mdbook::BookItem; use regex::{Captures, Regex}; use semver::{Version, VersionReq}; use std::collections::BTreeMap; -use std::io; +use std::fmt::Write as _; +use std::fs; +use std::io::{self, Write as _}; use std::path::PathBuf; -use std::process; +use std::process::{self, Command}; fn main() { let mut args = std::env::args().skip(1); @@ -57,10 +59,16 @@ struct Spec { deny_warnings: bool, rule_re: Regex, admonition_re: Regex, + std_link_re: Regex, + std_link_extract_re: Regex, } impl Spec { pub fn new() -> Spec { + // This is roughly a rustdoc intra-doc link definition. + let std_link = r"(?: [a-z]+@ )? + (?: std|core|alloc|proc_macro|test ) + (?: ::[A-Za-z_!:<>{}()\[\]]+ )?"; Spec { deny_warnings: std::env::var("SPEC_DENY_WARNINGS").as_deref() == Ok("1"), rule_re: Regex::new(r"(?m)^r\[([^]]+)]$").unwrap(), @@ -68,6 +76,21 @@ impl Spec { r"(?m)^ *> \[!(?[^]]+)\]\n(?
(?: *> .*\n)+)", ) .unwrap(), + std_link_re: Regex::new(&format!( + r"(?x) + (?: + ( \[`[^`]+`\] ) \( ({std_link}) \) + ) + | (?: + ( \[`{std_link}`\] ) + ) + " + )) + .unwrap(), + std_link_extract_re: Regex::new( + r#"
  • ]*href="(https://doc.rust-lang.org/[^"]+)""#, + ) + .unwrap(), } } @@ -152,6 +175,122 @@ impl Spec { }) .to_string() } + + /// Converts links to the standard library to the online documentation in + /// a fashion similar to rustdoc intra-doc links. + fn std_links(&self, chapter: &Chapter) -> String { + // This is very hacky, but should work well enough. + // + // Collect all standard library links. + // + // links are tuples of ("[`std::foo`]", None) for links without dest, + // or ("[`foo`]", "std::foo") with a dest. + let mut links: Vec<_> = self + .std_link_re + .captures_iter(&chapter.content) + .map(|cap| { + if let Some(no_dest) = cap.get(3) { + (no_dest.as_str(), None) + } else { + ( + cap.get(1).unwrap().as_str(), + Some(cap.get(2).unwrap().as_str()), + ) + } + }) + .collect(); + if links.is_empty() { + return chapter.content.clone(); + } + links.sort(); + links.dedup(); + + // Write a Rust source file to use with rustdoc to generate intra-doc links. + let tmp = tempfile::TempDir::with_prefix("mdbook-spec-").unwrap(); + let src_path = tmp.path().join("a.rs"); + // Allow redundant since there could some in-scope things that are + // technically not necessary, but we don't care about (like + // [`Option`](std::option::Option)). + let mut src = format!( + "#![deny(rustdoc::broken_intra_doc_links)]\n\ + #![allow(rustdoc::redundant_explicit_links)]\n" + ); + for (link, dest) in &links { + write!(src, "//! - {link}").unwrap(); + if let Some(dest) = dest { + write!(src, "({})", dest).unwrap(); + } + src.push('\n'); + } + writeln!( + src, + "extern crate alloc;\n\ + extern crate proc_macro;\n\ + extern crate test;\n" + ) + .unwrap(); + fs::write(&src_path, &src).unwrap(); + let output = Command::new("rustdoc") + .arg("--edition=2021") + .arg(&src_path) + .current_dir(tmp.path()) + .output() + .expect("rustdoc installed"); + if !output.status.success() { + eprintln!( + "error: failed to extract std links ({:?}) in chapter {} ({:?})\n", + output.status, + chapter.name, + chapter.source_path.as_ref().unwrap() + ); + io::stderr().write_all(&output.stderr).unwrap(); + process::exit(1); + } + + // Extract the links from the generated html. + let generated = + fs::read_to_string(tmp.path().join("doc/a/index.html")).expect("index.html generated"); + let urls: Vec<_> = self + .std_link_extract_re + .captures_iter(&generated) + .map(|cap| cap.get(1).unwrap().as_str()) + .collect(); + if urls.len() != links.len() { + eprintln!( + "error: expected rustdoc to generate {} links, but found {} in chapter {} ({:?})", + links.len(), + urls.len(), + chapter.name, + chapter.source_path.as_ref().unwrap() + ); + process::exit(1); + } + + // Replace any disambiguated links with just the disambiguation. + let mut output = self + .std_link_re + .replace_all(&chapter.content, |caps: &Captures| { + if let Some(dest) = caps.get(2) { + // Replace destination parenthesis with a link definition (square brackets). + format!("{}[{}]", &caps[1], dest.as_str()) + } else { + caps[0].to_string() + } + }) + .to_string(); + + // Append the link definitions to the bottom of the chapter. + write!(output, "\n").unwrap(); + for ((link, dest), url) in links.iter().zip(urls) { + if let Some(dest) = dest { + write!(output, "[{dest}]: {url}\n").unwrap(); + } else { + write!(output, "{link}: {url}\n").unwrap(); + } + } + + output + } } impl Preprocessor for Spec { @@ -170,6 +309,7 @@ impl Preprocessor for Spec { } ch.content = self.rule_definitions(&ch, &mut found_rules); ch.content = self.admonitions(&ch); + ch.content = self.std_links(&ch); } for section in &mut book.sections { let BookItem::Chapter(ch) = section else { From 0639c5803c6c4a7ed6cba33d4c6dc9116058a78c Mon Sep 17 00:00:00 2001 From: Eric Huss Date: Thu, 27 Jun 2024 08:04:28 -0700 Subject: [PATCH 03/21] Move regular expressions to be global statics. This just makes it a little easier to refer to them. --- mdbook-spec/Cargo.lock | 1 + mdbook-spec/Cargo.toml | 1 + mdbook-spec/src/main.rs | 78 ++++++++++++++++++++++------------------- 3 files changed, 44 insertions(+), 36 deletions(-) diff --git a/mdbook-spec/Cargo.lock b/mdbook-spec/Cargo.lock index d77a44025..971edafe2 100644 --- a/mdbook-spec/Cargo.lock +++ b/mdbook-spec/Cargo.lock @@ -388,6 +388,7 @@ version = "0.0.0" dependencies = [ "anyhow", "mdbook", + "once_cell", "pathdiff", "regex", "semver", diff --git a/mdbook-spec/Cargo.toml b/mdbook-spec/Cargo.toml index bbfd90342..50a4b3d3b 100644 --- a/mdbook-spec/Cargo.toml +++ b/mdbook-spec/Cargo.toml @@ -8,6 +8,7 @@ license = "MIT OR Apache-2.0" [dependencies] anyhow = "1.0.79" mdbook = { version = "0.4.36", default-features = false } +once_cell = "1.19.0" pathdiff = "0.2.1" regex = "1.10.3" semver = "1.0.21" diff --git a/mdbook-spec/src/main.rs b/mdbook-spec/src/main.rs index 7e617f064..f5506e96c 100644 --- a/mdbook-spec/src/main.rs +++ b/mdbook-spec/src/main.rs @@ -2,6 +2,7 @@ use mdbook::book::{Book, Chapter}; use mdbook::errors::Error; use mdbook::preprocess::{CmdPreprocessor, Preprocessor, PreprocessorContext}; use mdbook::BookItem; +use once_cell::sync::Lazy; use regex::{Captures, Regex}; use semver::{Version, VersionReq}; use std::collections::BTreeMap; @@ -11,6 +12,40 @@ use std::io::{self, Write as _}; use std::path::PathBuf; use std::process::{self, Command}; +/// The Regex for rules like `r[foo]`. +static RULE_RE: Lazy = Lazy::new(|| Regex::new(r"(?m)^r\[([^]]+)]$").unwrap()); + +/// The Regex for the syntax for blockquotes that have a specific CSS class, +/// like `> [!WARNING]`. +static ADMONITION_RE: Lazy = Lazy::new(|| { + Regex::new(r"(?m)^ *> \[!(?[^]]+)\]\n(?
    (?: *> .*\n)+)").unwrap() +}); + +/// A markdown link (without the brackets) that might possibly be a link to +/// the standard library using rustdoc's intra-doc notation. +const STD_LINK: &str = r"(?: [a-z]+@ )? + (?: std|core|alloc|proc_macro|test ) + (?: ::[A-Za-z_!:<>{}()\[\]]+ )?"; + +/// The Regex for a markdown link that might be a link to the standard library. +static STD_LINK_RE: Lazy = Lazy::new(|| { + Regex::new(&format!( + r"(?x) + (?: + ( \[`[^`]+`\] ) \( ({STD_LINK}) \) + ) + | (?: + ( \[`{STD_LINK}`\] ) + ) + " + )) + .unwrap() +}); + +/// The Regex used to extract the std links from the HTML generated by rustdoc. +static STD_LINK_EXTRACT_RE: Lazy = + Lazy::new(|| Regex::new(r#"
  • ]*href="(https://doc.rust-lang.org/[^"]+)""#).unwrap()); + fn main() { let mut args = std::env::args().skip(1); match args.next().as_deref() { @@ -56,41 +91,15 @@ fn handle_preprocessing(pre: &dyn Preprocessor) -> Result<(), Error> { } struct Spec { + /// Whether or not warnings should be errors (set by SPEC_DENY_WARNINGS + /// environment variable). deny_warnings: bool, - rule_re: Regex, - admonition_re: Regex, - std_link_re: Regex, - std_link_extract_re: Regex, } impl Spec { pub fn new() -> Spec { - // This is roughly a rustdoc intra-doc link definition. - let std_link = r"(?: [a-z]+@ )? - (?: std|core|alloc|proc_macro|test ) - (?: ::[A-Za-z_!:<>{}()\[\]]+ )?"; Spec { deny_warnings: std::env::var("SPEC_DENY_WARNINGS").as_deref() == Ok("1"), - rule_re: Regex::new(r"(?m)^r\[([^]]+)]$").unwrap(), - admonition_re: Regex::new( - r"(?m)^ *> \[!(?[^]]+)\]\n(?
    (?: *> .*\n)+)", - ) - .unwrap(), - std_link_re: Regex::new(&format!( - r"(?x) - (?: - ( \[`[^`]+`\] ) \( ({std_link}) \) - ) - | (?: - ( \[`{std_link}`\] ) - ) - " - )) - .unwrap(), - std_link_extract_re: Regex::new( - r#"
  • ]*href="(https://doc.rust-lang.org/[^"]+)""#, - ) - .unwrap(), } } @@ -103,7 +112,7 @@ impl Spec { ) -> String { let source_path = chapter.source_path.clone().unwrap_or_default(); let path = chapter.path.clone().unwrap_or_default(); - self.rule_re + RULE_RE .replace_all(&chapter.content, |caps: &Captures| { let rule_id = &caps[1]; if let Some((old, _)) = @@ -165,7 +174,7 @@ impl Spec { /// be a CSS class is valid. The actual styling needs to be added in a CSS /// file. fn admonitions(&self, chapter: &Chapter) -> String { - self.admonition_re + ADMONITION_RE .replace_all(&chapter.content, |caps: &Captures| { let lower = caps["admon"].to_lowercase(); format!( @@ -185,8 +194,7 @@ impl Spec { // // links are tuples of ("[`std::foo`]", None) for links without dest, // or ("[`foo`]", "std::foo") with a dest. - let mut links: Vec<_> = self - .std_link_re + let mut links: Vec<_> = STD_LINK_RE .captures_iter(&chapter.content) .map(|cap| { if let Some(no_dest) = cap.get(3) { @@ -250,8 +258,7 @@ impl Spec { // Extract the links from the generated html. let generated = fs::read_to_string(tmp.path().join("doc/a/index.html")).expect("index.html generated"); - let urls: Vec<_> = self - .std_link_extract_re + let urls: Vec<_> = STD_LINK_EXTRACT_RE .captures_iter(&generated) .map(|cap| cap.get(1).unwrap().as_str()) .collect(); @@ -267,8 +274,7 @@ impl Spec { } // Replace any disambiguated links with just the disambiguation. - let mut output = self - .std_link_re + let mut output = STD_LINK_RE .replace_all(&chapter.content, |caps: &Captures| { if let Some(dest) = caps.get(2) { // Replace destination parenthesis with a link definition (square brackets). From 2a35e3d5f26a8ccdba688813308c9817cf9cb87b Mon Sep 17 00:00:00 2001 From: Eric Huss Date: Thu, 27 Jun 2024 08:05:03 -0700 Subject: [PATCH 04/21] Fix std links that have digits in them. --- mdbook-spec/src/main.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mdbook-spec/src/main.rs b/mdbook-spec/src/main.rs index f5506e96c..69d762b4d 100644 --- a/mdbook-spec/src/main.rs +++ b/mdbook-spec/src/main.rs @@ -25,7 +25,7 @@ static ADMONITION_RE: Lazy = Lazy::new(|| { /// the standard library using rustdoc's intra-doc notation. const STD_LINK: &str = r"(?: [a-z]+@ )? (?: std|core|alloc|proc_macro|test ) - (?: ::[A-Za-z_!:<>{}()\[\]]+ )?"; + (?: ::[A-Za-z0-9_!:<>{}()\[\]]+ )?"; /// The Regex for a markdown link that might be a link to the standard library. static STD_LINK_RE: Lazy = Lazy::new(|| { From eaf37b03377a2bb4df19d81d89110a0f075eea01 Mon Sep 17 00:00:00 2001 From: Eric Huss Date: Thu, 27 Jun 2024 08:06:09 -0700 Subject: [PATCH 05/21] Move std_links to its own module. This code is getting long enough that it will help to organize it separately. --- mdbook-spec/src/main.rs | 148 ++--------------------------------- mdbook-spec/src/std_links.rs | 145 ++++++++++++++++++++++++++++++++++ 2 files changed, 150 insertions(+), 143 deletions(-) create mode 100644 mdbook-spec/src/std_links.rs diff --git a/mdbook-spec/src/main.rs b/mdbook-spec/src/main.rs index 69d762b4d..a12e95cc3 100644 --- a/mdbook-spec/src/main.rs +++ b/mdbook-spec/src/main.rs @@ -6,11 +6,11 @@ use once_cell::sync::Lazy; use regex::{Captures, Regex}; use semver::{Version, VersionReq}; use std::collections::BTreeMap; -use std::fmt::Write as _; -use std::fs; -use std::io::{self, Write as _}; +use std::io; use std::path::PathBuf; -use std::process::{self, Command}; +use std::process; + +mod std_links; /// The Regex for rules like `r[foo]`. static RULE_RE: Lazy = Lazy::new(|| Regex::new(r"(?m)^r\[([^]]+)]$").unwrap()); @@ -21,31 +21,6 @@ static ADMONITION_RE: Lazy = Lazy::new(|| { Regex::new(r"(?m)^ *> \[!(?[^]]+)\]\n(?
    (?: *> .*\n)+)").unwrap() }); -/// A markdown link (without the brackets) that might possibly be a link to -/// the standard library using rustdoc's intra-doc notation. -const STD_LINK: &str = r"(?: [a-z]+@ )? - (?: std|core|alloc|proc_macro|test ) - (?: ::[A-Za-z0-9_!:<>{}()\[\]]+ )?"; - -/// The Regex for a markdown link that might be a link to the standard library. -static STD_LINK_RE: Lazy = Lazy::new(|| { - Regex::new(&format!( - r"(?x) - (?: - ( \[`[^`]+`\] ) \( ({STD_LINK}) \) - ) - | (?: - ( \[`{STD_LINK}`\] ) - ) - " - )) - .unwrap() -}); - -/// The Regex used to extract the std links from the HTML generated by rustdoc. -static STD_LINK_EXTRACT_RE: Lazy = - Lazy::new(|| Regex::new(r#"
  • ]*href="(https://doc.rust-lang.org/[^"]+)""#).unwrap()); - fn main() { let mut args = std::env::args().skip(1); match args.next().as_deref() { @@ -184,119 +159,6 @@ impl Spec { }) .to_string() } - - /// Converts links to the standard library to the online documentation in - /// a fashion similar to rustdoc intra-doc links. - fn std_links(&self, chapter: &Chapter) -> String { - // This is very hacky, but should work well enough. - // - // Collect all standard library links. - // - // links are tuples of ("[`std::foo`]", None) for links without dest, - // or ("[`foo`]", "std::foo") with a dest. - let mut links: Vec<_> = STD_LINK_RE - .captures_iter(&chapter.content) - .map(|cap| { - if let Some(no_dest) = cap.get(3) { - (no_dest.as_str(), None) - } else { - ( - cap.get(1).unwrap().as_str(), - Some(cap.get(2).unwrap().as_str()), - ) - } - }) - .collect(); - if links.is_empty() { - return chapter.content.clone(); - } - links.sort(); - links.dedup(); - - // Write a Rust source file to use with rustdoc to generate intra-doc links. - let tmp = tempfile::TempDir::with_prefix("mdbook-spec-").unwrap(); - let src_path = tmp.path().join("a.rs"); - // Allow redundant since there could some in-scope things that are - // technically not necessary, but we don't care about (like - // [`Option`](std::option::Option)). - let mut src = format!( - "#![deny(rustdoc::broken_intra_doc_links)]\n\ - #![allow(rustdoc::redundant_explicit_links)]\n" - ); - for (link, dest) in &links { - write!(src, "//! - {link}").unwrap(); - if let Some(dest) = dest { - write!(src, "({})", dest).unwrap(); - } - src.push('\n'); - } - writeln!( - src, - "extern crate alloc;\n\ - extern crate proc_macro;\n\ - extern crate test;\n" - ) - .unwrap(); - fs::write(&src_path, &src).unwrap(); - let output = Command::new("rustdoc") - .arg("--edition=2021") - .arg(&src_path) - .current_dir(tmp.path()) - .output() - .expect("rustdoc installed"); - if !output.status.success() { - eprintln!( - "error: failed to extract std links ({:?}) in chapter {} ({:?})\n", - output.status, - chapter.name, - chapter.source_path.as_ref().unwrap() - ); - io::stderr().write_all(&output.stderr).unwrap(); - process::exit(1); - } - - // Extract the links from the generated html. - let generated = - fs::read_to_string(tmp.path().join("doc/a/index.html")).expect("index.html generated"); - let urls: Vec<_> = STD_LINK_EXTRACT_RE - .captures_iter(&generated) - .map(|cap| cap.get(1).unwrap().as_str()) - .collect(); - if urls.len() != links.len() { - eprintln!( - "error: expected rustdoc to generate {} links, but found {} in chapter {} ({:?})", - links.len(), - urls.len(), - chapter.name, - chapter.source_path.as_ref().unwrap() - ); - process::exit(1); - } - - // Replace any disambiguated links with just the disambiguation. - let mut output = STD_LINK_RE - .replace_all(&chapter.content, |caps: &Captures| { - if let Some(dest) = caps.get(2) { - // Replace destination parenthesis with a link definition (square brackets). - format!("{}[{}]", &caps[1], dest.as_str()) - } else { - caps[0].to_string() - } - }) - .to_string(); - - // Append the link definitions to the bottom of the chapter. - write!(output, "\n").unwrap(); - for ((link, dest), url) in links.iter().zip(urls) { - if let Some(dest) = dest { - write!(output, "[{dest}]: {url}\n").unwrap(); - } else { - write!(output, "{link}: {url}\n").unwrap(); - } - } - - output - } } impl Preprocessor for Spec { @@ -315,7 +177,7 @@ impl Preprocessor for Spec { } ch.content = self.rule_definitions(&ch, &mut found_rules); ch.content = self.admonitions(&ch); - ch.content = self.std_links(&ch); + ch.content = std_links::std_links(&ch); } for section in &mut book.sections { let BookItem::Chapter(ch) = section else { diff --git a/mdbook-spec/src/std_links.rs b/mdbook-spec/src/std_links.rs new file mode 100644 index 000000000..e0bc16c35 --- /dev/null +++ b/mdbook-spec/src/std_links.rs @@ -0,0 +1,145 @@ +use mdbook::book::Chapter; +use once_cell::sync::Lazy; +use regex::{Captures, Regex}; +use std::fmt::Write as _; +use std::fs; +use std::io::{self, Write as _}; +use std::process::{self, Command}; + +/// A markdown link (without the brackets) that might possibly be a link to +/// the standard library using rustdoc's intra-doc notation. +const STD_LINK: &str = r"(?: [a-z]+@ )? + (?: std|core|alloc|proc_macro|test ) + (?: ::[A-Za-z0-9_!:<>{}()\[\]]+ )?"; + +/// The Regex for a markdown link that might be a link to the standard library. +static STD_LINK_RE: Lazy = Lazy::new(|| { + Regex::new(&format!( + r"(?x) + (?: + ( \[`[^`]+`\] ) \( ({STD_LINK}) \) + ) + | (?: + ( \[`{STD_LINK}`\] ) + ) + " + )) + .unwrap() +}); + +/// The Regex used to extract the std links from the HTML generated by rustdoc. +static STD_LINK_EXTRACT_RE: Lazy = + Lazy::new(|| Regex::new(r#"
  • ]*href="(https://doc.rust-lang.org/[^"]+)""#).unwrap()); + +/// Converts links to the standard library to the online documentation in a +/// fashion similar to rustdoc intra-doc links. +pub fn std_links(chapter: &Chapter) -> String { + // This is very hacky, but should work well enough. + // + // Collect all standard library links. + // + // links are tuples of ("[`std::foo`]", None) for links without dest, + // or ("[`foo`]", "std::foo") with a dest. + let mut links: Vec<_> = STD_LINK_RE + .captures_iter(&chapter.content) + .map(|cap| { + if let Some(no_dest) = cap.get(3) { + (no_dest.as_str(), None) + } else { + ( + cap.get(1).unwrap().as_str(), + Some(cap.get(2).unwrap().as_str()), + ) + } + }) + .collect(); + if links.is_empty() { + return chapter.content.clone(); + } + links.sort(); + links.dedup(); + + // Write a Rust source file to use with rustdoc to generate intra-doc links. + let tmp = tempfile::TempDir::with_prefix("mdbook-spec-").unwrap(); + let src_path = tmp.path().join("a.rs"); + // Allow redundant since there could some in-scope things that are + // technically not necessary, but we don't care about (like + // [`Option`](std::option::Option)). + let mut src = format!( + "#![deny(rustdoc::broken_intra_doc_links)]\n\ + #![allow(rustdoc::redundant_explicit_links)]\n" + ); + for (link, dest) in &links { + write!(src, "//! - {link}").unwrap(); + if let Some(dest) = dest { + write!(src, "({})", dest).unwrap(); + } + src.push('\n'); + } + writeln!( + src, + "extern crate alloc;\n\ + extern crate proc_macro;\n\ + extern crate test;\n" + ) + .unwrap(); + fs::write(&src_path, &src).unwrap(); + let output = Command::new("rustdoc") + .arg("--edition=2021") + .arg(&src_path) + .current_dir(tmp.path()) + .output() + .expect("rustdoc installed"); + if !output.status.success() { + eprintln!( + "error: failed to extract std links ({:?}) in chapter {} ({:?})\n", + output.status, + chapter.name, + chapter.source_path.as_ref().unwrap() + ); + io::stderr().write_all(&output.stderr).unwrap(); + process::exit(1); + } + + // Extract the links from the generated html. + let generated = + fs::read_to_string(tmp.path().join("doc/a/index.html")).expect("index.html generated"); + let urls: Vec<_> = STD_LINK_EXTRACT_RE + .captures_iter(&generated) + .map(|cap| cap.get(1).unwrap().as_str()) + .collect(); + if urls.len() != links.len() { + eprintln!( + "error: expected rustdoc to generate {} links, but found {} in chapter {} ({:?})", + links.len(), + urls.len(), + chapter.name, + chapter.source_path.as_ref().unwrap() + ); + process::exit(1); + } + + // Replace any disambiguated links with just the disambiguation. + let mut output = STD_LINK_RE + .replace_all(&chapter.content, |caps: &Captures| { + if let Some(dest) = caps.get(2) { + // Replace destination parenthesis with a link definition (square brackets). + format!("{}[{}]", &caps[1], dest.as_str()) + } else { + caps[0].to_string() + } + }) + .to_string(); + + // Append the link definitions to the bottom of the chapter. + write!(output, "\n").unwrap(); + for ((link, dest), url) in links.iter().zip(urls) { + if let Some(dest) = dest { + write!(output, "[{dest}]: {url}\n").unwrap(); + } else { + write!(output, "{link}: {url}\n").unwrap(); + } + } + + output +} From ef165e6d3d7b177e515fa134d8554e5dbeabf8a7 Mon Sep 17 00:00:00 2001 From: Eric Huss Date: Thu, 27 Jun 2024 08:11:46 -0700 Subject: [PATCH 06/21] Split std_links into multiple functions. The std_links function was getting a little long, this should help organize a little. --- mdbook-spec/src/std_links.rs | 124 ++++++++++++++++++++--------------- 1 file changed, 72 insertions(+), 52 deletions(-) diff --git a/mdbook-spec/src/std_links.rs b/mdbook-spec/src/std_links.rs index e0bc16c35..1d381428a 100644 --- a/mdbook-spec/src/std_links.rs +++ b/mdbook-spec/src/std_links.rs @@ -5,6 +5,7 @@ use std::fmt::Write as _; use std::fs; use std::io::{self, Write as _}; use std::process::{self, Command}; +use tempfile::TempDir; /// A markdown link (without the brackets) that might possibly be a link to /// the standard library using rustdoc's intra-doc notation. @@ -34,12 +35,65 @@ static STD_LINK_EXTRACT_RE: Lazy = /// Converts links to the standard library to the online documentation in a /// fashion similar to rustdoc intra-doc links. pub fn std_links(chapter: &Chapter) -> String { - // This is very hacky, but should work well enough. - // - // Collect all standard library links. - // - // links are tuples of ("[`std::foo`]", None) for links without dest, - // or ("[`foo`]", "std::foo") with a dest. + let links = collect_markdown_links(chapter); + if links.is_empty() { + return chapter.content.clone(); + } + + // Write a Rust source file to use with rustdoc to generate intra-doc links. + let tmp = TempDir::with_prefix("mdbook-spec-").unwrap(); + run_rustdoc(&tmp, &links, &chapter); + + // Extract the links from the generated html. + let generated = + fs::read_to_string(tmp.path().join("doc/a/index.html")).expect("index.html generated"); + let urls: Vec<_> = STD_LINK_EXTRACT_RE + .captures_iter(&generated) + .map(|cap| cap.get(1).unwrap().as_str()) + .collect(); + if urls.len() != links.len() { + eprintln!( + "error: expected rustdoc to generate {} links, but found {} in chapter {} ({:?})", + links.len(), + urls.len(), + chapter.name, + chapter.source_path.as_ref().unwrap() + ); + process::exit(1); + } + + // Replace any disambiguated links with just the disambiguation. + let mut output = STD_LINK_RE + .replace_all(&chapter.content, |caps: &Captures| { + if let Some(dest) = caps.get(2) { + // Replace destination parenthesis with a link definition (square brackets). + format!("{}[{}]", &caps[1], dest.as_str()) + } else { + caps[0].to_string() + } + }) + .to_string(); + + // Append the link definitions to the bottom of the chapter. + write!(output, "\n").unwrap(); + for ((link, dest), url) in links.iter().zip(urls) { + if let Some(dest) = dest { + write!(output, "[{dest}]: {url}\n").unwrap(); + } else { + write!(output, "{link}: {url}\n").unwrap(); + } + } + + output +} + +/// Collects all markdown links. +/// +/// Returns a `Vec` of `(link, Option)` where markdown text like +/// ``[`std::fmt`]`` would return that as a link. The dest is optional, for +/// example ``[`Option`](std::option::Option)`` would have the part in +/// parentheses as the dest. +fn collect_markdown_links(chapter: &Chapter) -> Vec<(&str, Option<&str>)> { let mut links: Vec<_> = STD_LINK_RE .captures_iter(&chapter.content) .map(|cap| { @@ -54,13 +108,21 @@ pub fn std_links(chapter: &Chapter) -> String { }) .collect(); if links.is_empty() { - return chapter.content.clone(); + return vec![]; } links.sort(); links.dedup(); + links +} - // Write a Rust source file to use with rustdoc to generate intra-doc links. - let tmp = tempfile::TempDir::with_prefix("mdbook-spec-").unwrap(); +/// Generates links using rustdoc. +/// +/// This takes the given links and creates a temporary Rust source file +/// containing those links within doc-comments, and then runs rustdoc to +/// generate intra-doc links on them. +/// +/// The output will be in the given `tmp` directory. +fn run_rustdoc(tmp: &TempDir, links: &[(&str, Option<&str>)], chapter: &Chapter) { let src_path = tmp.path().join("a.rs"); // Allow redundant since there could some in-scope things that are // technically not necessary, but we don't care about (like @@ -69,7 +131,7 @@ pub fn std_links(chapter: &Chapter) -> String { "#![deny(rustdoc::broken_intra_doc_links)]\n\ #![allow(rustdoc::redundant_explicit_links)]\n" ); - for (link, dest) in &links { + for (link, dest) in links { write!(src, "//! - {link}").unwrap(); if let Some(dest) = dest { write!(src, "({})", dest).unwrap(); @@ -100,46 +162,4 @@ pub fn std_links(chapter: &Chapter) -> String { io::stderr().write_all(&output.stderr).unwrap(); process::exit(1); } - - // Extract the links from the generated html. - let generated = - fs::read_to_string(tmp.path().join("doc/a/index.html")).expect("index.html generated"); - let urls: Vec<_> = STD_LINK_EXTRACT_RE - .captures_iter(&generated) - .map(|cap| cap.get(1).unwrap().as_str()) - .collect(); - if urls.len() != links.len() { - eprintln!( - "error: expected rustdoc to generate {} links, but found {} in chapter {} ({:?})", - links.len(), - urls.len(), - chapter.name, - chapter.source_path.as_ref().unwrap() - ); - process::exit(1); - } - - // Replace any disambiguated links with just the disambiguation. - let mut output = STD_LINK_RE - .replace_all(&chapter.content, |caps: &Captures| { - if let Some(dest) = caps.get(2) { - // Replace destination parenthesis with a link definition (square brackets). - format!("{}[{}]", &caps[1], dest.as_str()) - } else { - caps[0].to_string() - } - }) - .to_string(); - - // Append the link definitions to the bottom of the chapter. - write!(output, "\n").unwrap(); - for ((link, dest), url) in links.iter().zip(urls) { - if let Some(dest) = dest { - write!(output, "[{dest}]: {url}\n").unwrap(); - } else { - write!(output, "{link}: {url}\n").unwrap(); - } - } - - output } From 994073fc0f3901ed7fab22ccb09778e20fc9aef2 Mon Sep 17 00:00:00 2001 From: Eric Huss Date: Thu, 27 Jun 2024 08:15:41 -0700 Subject: [PATCH 07/21] Don't override explicit link definitions. If the author has an explicit link definition, don't allow std_links to override it. --- mdbook-spec/src/std_links.rs | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/mdbook-spec/src/std_links.rs b/mdbook-spec/src/std_links.rs index 1d381428a..d3f1c2ad7 100644 --- a/mdbook-spec/src/std_links.rs +++ b/mdbook-spec/src/std_links.rs @@ -1,6 +1,7 @@ use mdbook::book::Chapter; use once_cell::sync::Lazy; use regex::{Captures, Regex}; +use std::collections::HashSet; use std::fmt::Write as _; use std::fs; use std::io::{self, Write as _}; @@ -32,6 +33,15 @@ static STD_LINK_RE: Lazy = Lazy::new(|| { static STD_LINK_EXTRACT_RE: Lazy = Lazy::new(|| Regex::new(r#"
  • ]*href="(https://doc.rust-lang.org/[^"]+)""#).unwrap()); +/// The Regex for a markdown link definition. +static LINK_DEF_RE: Lazy = Lazy::new(|| { + // This is a pretty lousy regex for a link definition. It doesn't + // handle things like blockquotes, code blocks, etc. Using a + // markdown parser isn't really feasible here, it would be nice to + // improve this. + Regex::new(r#"(?m)^(?