feat: render AsciiDoc prose with acdc
commit
b2f9dd0feat: 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
Reviews
No reviews of this commit yet — record a verdict below.
Start a review
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()
+}