git-ents.gitmain
⌘K
foforge
commit b2f9dd0
feat: render AsciiDoc prose with acdc

Adopt acdc as the forge’s AsciiDoc engine: a repository’s README.adoc becomes the rendered centerpiece of its overview, and AsciiDoc blobs display as formatted documents rather than highlighted source.

feat: add acdc-parser/acdc-converters-html git dependencies feat: add asciidoc module converting AsciiDoc to embedded HTML feat: render a repository’s README.adoc on its overview page feat: render .adoc/.asciidoc blobs as formatted documents Assisted-by: Claude:claude-opus-4-8

Joseph D. Carpinelli · 1 month ago

Reviews

No reviews of this commit yet — record a verdict below.

Start a review

verdict

Cargo.lock @@ -2,6 +2,49 @@ # It is not intended for manual editing. version = 4 +[[package]] +name = "acdc-converters-core" +version = "0.1.0" +source = "git+https://github.com/nlopes/acdc?rev=3452a4a8d745e6279e50e1d465551b2e33160464#3452a4a8d745e6279e50e1d465551b2e33160464" +dependencies = [ + "acdc-parser", + "bitflags", + "thiserror", + "tracing", +] + +[[package]] +name = "acdc-converters-html" +version = "0.1.0" +source = "git+https://github.com/nlopes/acdc?rev=3452a4a8d745e6279e50e1d465551b2e33160464#3452a4a8d745e6279e50e1d465551b2e33160464" +dependencies = [ + "acdc-converters-core", + "acdc-parser", + "chrono", + "thiserror", + "tracing", +] + +[[package]] +name = "acdc-parser" +version = "0.9.0" +source = "git+https://github.com/nlopes/acdc?rev=3452a4a8d745e6279e50e1d465551b2e33160464#3452a4a8d745e6279e50e1d465551b2e33160464" +dependencies = [ + "bitflags", + "bumpalo", + "csv", + "encoding_rs", + "evalexpr", + "peg", + "rustc-hash", + "self_cell", + "serde", + "serde_json", + "thiserror", + "tracing", + "url", +] + [[package]] name = "aho-corasick" version = "1.1.4" @@ -11,6 +54,15 @@ "memchr", ] +[[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 = "1.0.0" @@ -319,6 +371,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" +[[package]] +name = "autocfg" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53" + [[package]] name = "axum" version = "0.8.9" @@ -377,6 +435,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4388bee8683e3d04af747c73422af53102d2bd24d9eadb6cbc100baef4b43f8" +[[package]] +name = "bumpalo" +version = "3.20.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72f5acc6cb2ba439de613abc23857ec3d78374d8ed5ac84e9d11336e87da8649" + [[package]] name = "bytes" version = "1.12.0" @@ -399,6 +463,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" +[[package]] +name = "chrono" +version = "0.4.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1aa79e62e7697b8e29b513a68abacf485adcd1fe8284a4316c5ae868e6633327" +dependencies = [ + "iana-time-zone", + "js-sys", + "num-traits", + "wasm-bindgen", + "windows-link", +] + [[package]] name = "clap" version = "4.6.1" @@ -455,6 +532,44 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1d07550c9036bf2ae0c684c4297d503f838287c83c53686d05370d0e139ae570" +[[package]] +name = "core-foundation-sys" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" + +[[package]] +name = "csv" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52cd9d68cf7efc6ddfaaee42e7288d3a99d613d4b50f76ce9827ae0c6e14f938" +dependencies = [ + "csv-core", + "itoa", + "ryu", + "serde_core", +] + +[[package]] +name = "csv-core" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "704a3c26996a80471189265814dbc2c257598b96b8a7feae2d31ace646bb9782" +dependencies = [ + "memchr", +] + +[[package]] +name = "displaydoc" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ac70aa55017e108007fbaf5aa0f54b021c98f92ff8af59d42eda9da96e3dd4f" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "dlmalloc" version = "0.2.14" @@ -466,6 +581,15 @@ "windows-sys", ] +[[package]] +name = "encoding_rs" +version = "0.8.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3" +dependencies = [ + "cfg-if", +] + [[package]] name = "equivalent" version = "1.0.2" @@ -482,6 +606,12 @@ "windows-sys", ] +[[package]] +name = "evalexpr" +version = "13.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25929004897f2bbab309121a60400d36992f6d911d09baa6c172f6cc55706601" + [[package]] name = "fastrand" version = "2.4.1" @@ -573,6 +703,9 @@ name = "git-ents-server" version = "0.0.0" dependencies = [ + "acdc-converters-core", + "acdc-converters-html", + "acdc-parser", "arborium", "axum", "clap", @@ -681,6 +814,133 @@ "tower-service", ] +[[package]] +name = "iana-time-zone" +version = "0.1.65" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e31bc9ad994ba00e440a8aa5c9ef0ec67d5cb5e5cb0cc7f8b744a35b389cc470" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "log", + "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 = "icu_collections" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2984d1cd16c883d7935b9e07e44071dca8d917fd52ecc02c04d5fa0b5a3f191c" +dependencies = [ + "displaydoc", + "potential_utf", + "utf8_iter", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_locale_core" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92219b62b3e2b4d88ac5119f8904c10f8f61bf7e95b640d25ba3075e6cac2c29" +dependencies = [ + "displaydoc", + "litemap", + "tinystr", + "writeable", + "zerovec", +] + +[[package]] +name = "icu_normalizer" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c56e5ee99d6e3d33bd91c5d85458b6005a22140021cc324cea84dd0e72cff3b4" +dependencies = [ + "icu_collections", + "icu_normalizer_data", + "icu_properties", + "icu_provider", + "smallvec", + "zerovec", +] + +[[package]] +name = "icu_normalizer_data" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da3be0ae77ea334f4da67c12f149704f19f81d1adf7c51cf482943e84a2bad38" + +[[package]] +name = "icu_properties" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bee3b67d0ea5c2cca5003417989af8996f8604e34fb9ddf96208a033901e70de" +dependencies = [ + "icu_collections", + "icu_locale_core", + "icu_properties_data", + "icu_provider", + "zerotrie", + "zerovec", +] + +[[package]] +name = "icu_properties_data" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e2bbb201e0c04f7b4b3e14382af113e17ba4f63e2c9d2ee626b720cbce54a14" + +[[package]] +name = "icu_provider" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "139c4cf31c8b5f33d7e199446eff9c1e02decfc2f0eec2c8d71f65befa45b421" +dependencies = [ + "displaydoc", + "icu_locale_core", + "writeable", + "yoke", + "zerofrom", + "zerotrie", + "zerovec", +] + +[[package]] +name = "idna" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b0875f23caa03898994f6ddc501886a45c7d3d62d04d2d90788d47be1b1e4de" +dependencies = [ + "idna_adapter", + "smallvec", + "utf8_iter", +] + +[[package]] +name = "idna_adapter" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb68373c0d6620ef8105e855e7745e18b0d00d3bdb07fb532e434244cdb9a714" +dependencies = [ + "icu_normalizer", + "icu_properties", +] + [[package]] name = "indexmap" version = "2.14.0" @@ -703,6 +963,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" +[[package]] +name = "js-sys" +version = "0.3.102" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03d04c30968dffe80775bd4d7fb676131cd04a1fb46d2686dbffbaec2d9dfd31" +dependencies = [ + "cfg-if", + "futures-util", + "wasm-bindgen", +] + [[package]] name = "libc" version = "0.2.186" @@ -715,6 +986,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53" +[[package]] +name = "litemap" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92daf443525c4cce67b150400bc2316076100ce0b3686209eb8cf3c31612e6f0" + [[package]] name = "log" version = "0.4.33" @@ -774,6 +1051,15 @@ "windows-sys", ] +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + [[package]] name = "once_cell" version = "1.21.4" @@ -786,6 +1072,33 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe" +[[package]] +name = "peg" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0aad070be5b63aa72103f2fcdd70a83adbd5e90112ce5b574171ff1c65501773" +dependencies = [ + "peg-macros", + "peg-runtime", +] + +[[package]] +name = "peg-macros" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddd8ef6825cae95355031ae26a99b616a2a21f22ba2de0197c43dfb05acbe7ee" +dependencies = [ + "peg-runtime", + "proc-macro2", + "quote", +] + +[[package]] +name = "peg-runtime" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7011d97b484a5ebdc4b1fdb3b12d5e4bbbea56e9d22b688f2e79e04b65a7d8a6" + [[package]] name = "percent-encoding" version = "2.3.2" @@ -798,6 +1111,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" +[[package]] +name = "potential_utf" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0103b1cef7ec0cf76490e969665504990193874ea05c85ff9bab8b911d0a0564" +dependencies = [ + "zerovec", +] + [[package]] name = "proc-macro-crate" version = "3.5.0" @@ -913,6 +1235,12 @@ "unicode-ident", ] +[[package]] +name = "rustc-hash" +version = "2.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94300abf3f1ae2e2b8ffb7b58043de3d399c73fa6f4b73826402a5c457614dbe" + [[package]] name = "rustc_version" version = "0.4.1" @@ -935,12 +1263,24 @@ "windows-sys", ] +[[package]] +name = "rustversion" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" + [[package]] name = "ryu" version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f" +[[package]] +name = "self_cell" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b12e76d157a900eb52e81bc6e9f3069344290341720e9178cde2407113ac8d89" + [[package]] name = "semver" version = "1.0.28" @@ -954,6 +1294,7 @@ checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" dependencies = [ "serde_core", + "serde_derive", ] [[package]] @@ -1050,6 +1391,12 @@ "windows-sys", ] +[[package]] +name = "stable_deref_trait" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" + [[package]] name = "streaming-iterator" version = "0.1.9" @@ -1079,6 +1426,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" +[[package]] +name = "synstructure" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "tempfile" version = "3.27.0" @@ -1092,6 +1450,36 @@ "windows-sys", ] +[[package]] +name = "thiserror" +version = "2.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tinystr" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8323304221c2a851516f22236c5722a72eaa19749016521d6dff0824447d96d" +dependencies = [ + "displaydoc", + "zerovec", +] + [[package]] name = "tokio" version = "1.52.3" @@ -1185,9 +1573,21 @@ dependencies = [ "log", "pin-project-lite", + "tracing-attributes", "tracing-core", ] +[[package]] +name = "tracing-attributes" +version = "0.1.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "tracing-core" version = "0.1.36" @@ -1209,6 +1609,24 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" +[[package]] +name = "url" +version = "2.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff67a8a4397373c3ef660812acab3268222035010ab8680ec4215f38ba3d0eed" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", + "serde", +] + +[[package]] +name = "utf8_iter" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" + [[package]] name = "utf8parse" version = "0.2.2" @@ -1227,12 +1645,110 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" +[[package]] +name = "wasm-bindgen" +version = "0.2.125" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ddb3f79143bced6de84270411622a2699cee572fc0875aeaf1e7867cf9fca1a" +dependencies = [ + "cfg-if", + "once_cell", + "rustversion", + "wasm-bindgen-macro", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.125" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e21a184b13fb19e157296e2c46056aec9092264fab83e4ba59e68c61b323c3d" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.125" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fecefd9c35bd935a20fc3fc344b5f29138961e4f47fb03297d88f2587afb5ebd" +dependencies = [ + "bumpalo", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.125" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23939e44bb9a5d7576fa2b563dc2e136628f1224e88a8deed09e04858b77871f" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "windows-core" +version = "0.62.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb" +dependencies = [ + "windows-implement", + "windows-interface", + "windows-link", + "windows-result", + "windows-strings", +] + +[[package]] +name = "windows-implement" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "windows-interface" +version = "0.59.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "windows-link" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" +[[package]] +name = "windows-result" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-strings" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091" +dependencies = [ + "windows-link", +] + [[package]] name = "windows-sys" version = "0.61.2" @@ -1251,6 +1767,89 @@ "memchr", ] +[[package]] +name = "writeable" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ffae5123b2d3fc086436f8834ae3ab053a283cfac8fe0a0b8eaae044768a4c4" + +[[package]] +name = "yoke" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "709fe23a0424b6a435d82152b1bd3fdfb0833487d5fa90d05d42762a9891fef5" +dependencies = [ + "stable_deref_trait", + "yoke-derive", + "zerofrom", +] + +[[package]] +name = "yoke-derive" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de844c262c8848816172cef550288e7dc6c7b7814b4ee56b3e1553f275f1858e" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + +[[package]] +name = "zerofrom" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ec05a11813ea801ff6d75110ad09cd0824ddba17dfe17128ea0d5f68e6c5272" +dependencies = [ + "zerofrom-derive", +] + +[[package]] +name = "zerofrom-derive" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11532158c46691caf0f2593ea8358fed6bbf68a0315e80aae9bd41fbade684a1" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + +[[package]] +name = "zerotrie" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f9152d31db0792fa83f70fb2f83148effb5c1f5b8c7686c3459e361d9bc20bf" +dependencies = [ + "displaydoc", + "yoke", + "zerofrom", +] + +[[package]] +name = "zerovec" +version = "0.11.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90f911cbc359ab6af17377d242225f4d75119aec87ea711a880987b18cd7b239" +dependencies = [ + "yoke", + "zerofrom", + "zerovec-derive", +] + +[[package]] +name = "zerovec-derive" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "625dc425cab0dca6dc3c3319506e6593dcb08a9f387ea3b284dbd52a92c40555" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "zmij" version = "1.0.21"
Cargo.toml @@ -29,6 +29,9 @@ "lang-html", "lang-css", ] } +acdc-parser = { git = "https://github.com/nlopes/acdc", rev = "3452a4a8d745e6279e50e1d465551b2e33160464" } +acdc-converters-core = { git = "https://github.com/nlopes/acdc", rev = "3452a4a8d745e6279e50e1d465551b2e33160464" } +acdc-converters-html = { git = "https://github.com/nlopes/acdc", rev = "3452a4a8d745e6279e50e1d465551b2e33160464" } axum = "0.8" clap = { version = "4.5.60", features = ["derive"] } clap_mangen = "0.2.31"
crates/git-ents-server/Cargo.toml @@ -6,6 +6,9 @@ license.workspace = true [dependencies] +acdc-parser = { workspace = true } +acdc-converters-core = { workspace = true } +acdc-converters-html = { workspace = true } arborium = { workspace = true } axum = { workspace = true } clap = { workspace = true }
crates/git-ents-server/src/main.rs @@ -1,5 +1,6 @@ //! Git Ents server — helpful guardians of your git trees. +mod asciidoc; mod http; mod web;
crates/git-ents-server/src/web.rs @@ -193,6 +193,28 @@ .commit-msg { font-family: var(--font-mono); font-size: .9rem; white-space: pre-wrap; word-break: break-word; } +.adoc-body { padding: 40px 48px 52px; max-width: 44rem; overflow-wrap: break-word; } +.adoc-body > :first-child { margin-top: 0; } +.adoc-body h1, .adoc-body h2, .adoc-body h3, .adoc-body h4 { font-family: var(--font-serif); font-weight: 700; letter-spacing: -.01em; line-height: 1.25; margin: 1.8rem 0 .9rem; } +.adoc-body h1 { font-size: 2.4rem; letter-spacing: -.02em; } +.adoc-body h2 { font-size: 1.4rem; font-weight: 600; position: relative; padding-bottom: .55rem; } +.adoc-body h2::after { content: ""; position: absolute; left: 0; bottom: 0; width: 3rem; height: 2px; background: var(--color-accent); border-radius: 1px; } +.adoc-body h3 { font-size: 1.15rem; font-weight: 600; } +.adoc-body p, .adoc-body ul, .adoc-body ol { margin: 0 0 1rem; } +.adoc-body ul, .adoc-body ol { padding-left: 1.4rem; } +.adoc-body li { margin: .25rem 0; } +.adoc-body a { font-weight: 500; } +.adoc-body code, .adoc-body .literal { font-family: var(--font-mono); font-size: .86em; background: var(--color-code-bg); padding: .1rem .35rem; border-radius: 5px; } +.adoc-body pre { font-family: var(--font-mono); font-size: .82rem; line-height: 1.55; background: var(--color-code-bg); border: 1px solid var(--color-border); border-radius: var(--radius-sm); padding: 1rem 1.2rem; overflow-x: auto; margin: 0 0 1rem; } +.adoc-body pre code { background: none; padding: 0; font-size: inherit; } +.adoc-body blockquote { border-left: 3px solid var(--color-accent); padding: .2rem 0 .2rem 1.1rem; margin: 0 0 1rem; color: var(--color-text-muted); } +.adoc-body table { border-collapse: collapse; margin: 0 0 1rem; font-size: .92rem; } +.adoc-body th, .adoc-body td { border: 1px solid var(--color-border); padding: .4rem .7rem; text-align: left; } +.adoc-body th { background: var(--color-code-bg); font-weight: 600; } +.adoc-body .title { font-weight: 600; color: var(--color-text-muted); font-size: .9rem; margin-bottom: .3rem; } +.adoc-body img { max-width: 100%; height: auto; } +.adoc-body hr { border: none; border-top: 1px solid var(--color-border); margin: 1.8rem 0; } + .site-footer { border-top: 1px solid var(--color-border); color: var(--color-text-muted); font-size: .8rem; margin-top: auto; } .footer-inner { max-width: var(--max-width); margin: 0 auto; padding: 2rem 1.5rem; text-align: center; } .footer-inner a { color: var(--color-text-muted); text-decoration: none; } @@ -316,6 +338,7 @@ .filter(|s| !s.is_empty()); let commits = recent_commits(repo).await; let tree = root_tree(repo, branch.is_some()).await; + let readme = readme(repo, &tree).await; let clone_url = clone_url(host, rel); let name = rel.rsplit('/').next().unwrap_or(rel); @@ -346,6 +369,12 @@ p { "Push a commit to get started." } } } @else { + @if let Some((file, html)) = &readme { + div.card { + div.card-header { (file) } + article.adoc-body { (PreEscaped(html)) } + } + } @if !tree.is_empty() { div.card { div.card-header { "Files" } @@ -430,6 +459,23 @@ list_tree(repo, "HEAD").await } +/// The rendered README for the overview: the first AsciiDoc file in the root +/// tree whose stem is `README`, converted to HTML, paired with its filename. +/// `None` when there is no such file or it fails to render. +async fn readme(repo: &Path, tree: &[TreeEntry]) -> Option<(String, String)> { + let entry = tree.iter().find(|e| { + !e.is_dir + && crate::asciidoc::is_asciidoc(&e.name) + && e.name + .rsplit_once('.') + .is_some_and(|(stem, _)| stem.eq_ignore_ascii_case("readme")) + })?; + let spec = format!("HEAD:{}", entry.name); + let bytes = git_output_bytes(repo, &["cat-file", "-p", &spec]).await?; + let html = crate::asciidoc::to_html(&String::from_utf8_lossy(&bytes))?; + Some((entry.name.clone(), html)) +} + /// The entries of the tree named by `spec` (a git tree-ish such as `HEAD` or /// `HEAD:src`), directories first then by name. Empty if `spec` is not a tree. async fn list_tree(repo: &Path, spec: &str) -> Vec<TreeEntry> { @@ -592,7 +638,14 @@ let body = if is_binary(&bytes) { html! { div.blob { div.binary { "Binary file (" (bytes.len()) " bytes) not shown." } } } } else { - blob_body(name, &String::from_utf8_lossy(&bytes)) + let text = String::from_utf8_lossy(&bytes); + match crate::asciidoc::is_asciidoc(name) + .then(|| crate::asciidoc::to_html(&text)) + .flatten() + { + Some(html) => html! { div.card { article.adoc-body { (PreEscaped(html)) } } }, + None => blob_body(name, &text), + } }; page( name,
crates/git-ents-server/src/asciidoc.rs @@ -1,0 +1,34 @@ +//! AsciiDoc rendering via the [`acdc`](https://github.com/nlopes/acdc) library. +//! +//! The forge treats AsciiDoc as its prose format: a repository's `README.adoc` +//! becomes the editorial centerpiece of the overview, and `.adoc`/`.asciidoc` +//! blobs render as formatted documents rather than highlighted source. Output is +//! the *embedded* fragment (no `<!DOCTYPE>`/`<html>` frame) so it can drop +//! straight into a card body styled by the page's own stylesheet. + +use acdc_converters_core::{Converter, Options as ConvertOptions}; +use acdc_converters_html::{Processor, RenderOptions}; +use acdc_parser::Options as ParseOptions; + +/// File extensions that name an AsciiDoc document. +const EXTENSIONS: [&str; 4] = ["adoc", "asciidoc", "asc", "adc"]; + +/// Whether `name` looks like an AsciiDoc file by its extension. +pub(crate) fn is_asciidoc(name: &str) -> bool { + name.rsplit_once('.') + .is_some_and(|(_, ext)| EXTENSIONS.iter().any(|e| ext.eq_ignore_ascii_case(e))) +} + +/// Render AsciiDoc `source` to an embedded HTML fragment, or `None` if it cannot +/// be parsed or converted. The fragment carries no document frame, so callers +/// place it inside their own container. +pub(crate) fn to_html(source: &str) -> Option<String> { + let parsed = acdc_parser::parse(source, &ParseOptions::default()).ok()?; + let doc = parsed.document(); + let processor = Processor::new(ConvertOptions::default(), doc.attributes.clone()); + let options = RenderOptions { + embedded: true, + ..RenderOptions::default() + }; + processor.convert_to_string(doc, &options).ok() +}