diff --git a/Cargo.lock b/Cargo.lock index 848e212d..5da7c286 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -45,7 +45,7 @@ dependencies = [ "actix-rt", "actix-service", "actix-utils", - "base64 0.22.1", + "base64", "bitflags", "brotli", "bytes", @@ -53,7 +53,7 @@ dependencies = [ "derive_more", "encoding_rs", "flate2", - "foldhash", + "foldhash 0.1.5", "futures-core", "h2 0.3.27", "http 0.2.12", @@ -74,57 +74,14 @@ dependencies = [ "zstd", ] -[[package]] -name = "actix-macros" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e01ed3140b2f8d422c68afa1ed2e85d996ea619c988ac834d255db32138655cb" -dependencies = [ - "quote", - "syn", -] - -[[package]] -name = "actix-router" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13d324164c51f63867b57e73ba5936ea151b8a41a1d23d1031eeb9f70d0236f8" -dependencies = [ - "bytestring", - "cfg-if", - "http 0.2.12", - "regex", - "regex-lite", - "serde", - "tracing", -] - [[package]] name = "actix-rt" version = "2.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "92589714878ca59a7626ea19734f0e07a6a875197eec751bb5d3f99e64998c63" dependencies = [ - "actix-macros", - "futures-core", - "tokio", -] - -[[package]] -name = "actix-server" -version = "2.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a65064ea4a457eaf07f2fba30b4c695bf43b721790e9530d26cb6f9019ff7502" -dependencies = [ - "actix-rt", - "actix-service", - "actix-utils", "futures-core", - "futures-util", - "mio", - "socket2 0.5.10", "tokio", - "tracing", ] [[package]] @@ -166,61 +123,6 @@ dependencies = [ "pin-project-lite", ] -[[package]] -name = "actix-web" -version = "4.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a597b77b5c6d6a1e1097fddde329a83665e25c5437c696a3a9a4aa514a614dea" -dependencies = [ - "actix-codec", - "actix-http", - "actix-macros", - "actix-router", - "actix-rt", - "actix-server", - "actix-service", - "actix-utils", - "actix-web-codegen", - "bytes", - "bytestring", - "cfg-if", - "cookie", - "derive_more", - "encoding_rs", - "foldhash", - "futures-core", - "futures-util", - "impl-more", - "itoa", - "language-tags", - "log", - "mime", - "once_cell", - "pin-project-lite", - "regex", - "regex-lite", - "serde", - "serde_json", - "serde_urlencoded", - "smallvec", - "socket2 0.5.10", - "time", - "tracing", - "url", -] - -[[package]] -name = "actix-web-codegen" -version = "4.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f591380e2e68490b5dfaf1dd1aa0ebe78d84ba7067078512b4ea6e4492d622b8" -dependencies = [ - "actix-router", - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "adler2" version = "2.0.1" @@ -234,7 +136,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75" dependencies = [ "cfg-if", - "getrandom 0.3.4", "once_cell", "version_check", "zerocopy", @@ -282,6 +183,36 @@ version = "1.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "69f7f8c3906b62b754cd5326047894316021dcfe5a194c8ea52bdd94934a3457" +[[package]] +name = "arrayvec" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" + +[[package]] +name = "async-compression" +version = "0.4.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93c1f86859c1af3d514fa19e8323147ff10ea98684e6c7b307912509f50e67b2" +dependencies = [ + "compression-codecs", + "compression-core", + "futures-core", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "async-trait" +version = "0.1.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "atomic-waker" version = "1.1.2" @@ -300,7 +231,7 @@ dependencies = [ "actix-service", "actix-tls", "actix-utils", - "base64 0.22.1", + "base64", "bytes", "cfg-if", "cookie", @@ -322,21 +253,79 @@ dependencies = [ ] [[package]] -name = "badgers" -version = "1.2.0" +name = "axum" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7954c1a712ad1b82f0f719bbeee7dc5ffd3d13a66c7dfb1e1df02f79e2729441" +checksum = "8a18ed336352031311f4e0b4dd2ff392d4fbb370777c9d18d7fc9d7359f73871" dependencies = [ - "ab_glyph", - "base64 0.21.7", - "once_cell", + "axum-core", + "axum-macros", + "bytes", + "form_urlencoded", + "futures-util", + "http 1.3.1", + "http-body", + "http-body-util", + "hyper", + "hyper-util", + "itoa", + "matchit", + "memchr", + "mime", + "percent-encoding", + "pin-project-lite", + "serde_core", + "serde_json", + "serde_path_to_error", + "serde_urlencoded", + "sync_wrapper", + "tokio", + "tower", + "tower-layer", + "tower-service", + "tracing", ] [[package]] -name = "base64" -version = "0.21.7" +name = "axum-core" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59446ce19cd142f8833f856eb31f3eb097812d1479ab224f54d72428ca21ea22" +dependencies = [ + "bytes", + "futures-core", + "http 1.3.1", + "http-body", + "http-body-util", + "mime", + "pin-project-lite", + "sync_wrapper", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "axum-macros" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "604fde5e028fea851ce1d8570bbdc034bec850d157f7569d10f347d06808c05c" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "badgers" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" +checksum = "07edf07a93f8d8bdef82aa987575e6de8765b72348daa4a83a1c7668702a4636" +dependencies = [ + "ab_glyph", + "base64", + "thiserror", +] [[package]] name = "base64" @@ -415,6 +404,12 @@ version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" +[[package]] +name = "bytesize" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f5c434ae3cf0089ca203e9019ebe529c47ff45cefe8af7c85ecb734ef541822f" + [[package]] name = "bytestring" version = "1.5.0" @@ -426,9 +421,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.2.44" +version = "1.2.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37521ac7aabe3d13122dc382493e20c9416f299d2ccd5b3a5340a2570cdeb0f3" +checksum = "35900b6c8d709fb1d854671ae27aeaa9eec2f8b01b364e1619a40da3e6fe2afe" dependencies = [ "find-msvc-tools", "jobserver", @@ -442,12 +437,35 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" +[[package]] +name = "cfg_aliases" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" + [[package]] name = "clru" version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cbd0f76e066e64fdc5631e3bb46381254deab9ef1158292f27c8c57e3bf3fe59" +[[package]] +name = "compression-codecs" +version = "0.4.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "680dc087785c5230f8e8843e2e57ac7c1c90488b6a91b88caa265410568f441b" +dependencies = [ + "compression-core", + "flate2", + "memchr", +] + +[[package]] +name = "compression-core" +version = "0.4.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a9b614a5787ef0c8802a55766480563cb3a93b435898c422ed2a359cf811582" + [[package]] name = "config" version = "0.15.18" @@ -505,6 +523,15 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "crossbeam-channel" +version = "0.5.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82b8f8f868b36967f9606790d1903570de9ceaf870a7bf9fbbd3016d636a2cb2" +dependencies = [ + "crossbeam-utils", +] + [[package]] name = "crossbeam-utils" version = "0.8.21" @@ -664,6 +691,12 @@ version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813" +[[package]] +name = "either" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" + [[package]] name = "encoding_rs" version = "0.8.35" @@ -755,19 +788,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" [[package]] -name = "foreign-types" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" -dependencies = [ - "foreign-types-shared", -] - -[[package]] -name = "foreign-types-shared" -version = "0.1.1" +name = "foldhash" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" +checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb" [[package]] name = "form_urlencoded" @@ -826,6 +850,19 @@ version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" +[[package]] +name = "futures-lite" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f78e10609fe0e0b3f4157ffab1876319b5b0db102a2c60dc4626306dc46b44ad" +dependencies = [ + "fastrand", + "futures-core", + "futures-io", + "parking", + "pin-project-lite", +] + [[package]] name = "futures-macro" version = "0.3.31" @@ -877,16 +914,6 @@ dependencies = [ "version_check", ] -[[package]] -name = "gethostname" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1ebd34e35c46e00bb73e81363248d627782724609fe1b6396f553f68fe3862e" -dependencies = [ - "libc", - "winapi", -] - [[package]] name = "getrandom" version = "0.2.16" @@ -894,8 +921,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" dependencies = [ "cfg-if", + "js-sys", "libc", "wasi", + "wasm-bindgen", ] [[package]] @@ -905,9 +934,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" dependencies = [ "cfg-if", + "js-sys", "libc", "r-efi", "wasip2", + "wasm-bindgen", ] [[package]] @@ -931,45 +962,45 @@ version = "0.71.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a61e71ec6817fc3c9f12f812682cfe51ee6ea0d2e27e02fc3849c35524617435" dependencies = [ - "gix-actor", - "gix-attributes", - "gix-command", - "gix-commitgraph", - "gix-config", - "gix-date", - "gix-diff", - "gix-dir", - "gix-discover", + "gix-actor 0.34.0", + "gix-attributes 0.25.0", + "gix-command 0.5.0", + "gix-commitgraph 0.27.0", + "gix-config 0.44.0", + "gix-date 0.9.4", + "gix-diff 0.51.0", + "gix-dir 0.13.0", + "gix-discover 0.39.0", "gix-features 0.41.1", - "gix-filter", + "gix-filter 0.18.0", "gix-fs 0.14.0", "gix-glob 0.19.0", "gix-hash 0.17.0", - "gix-hashtable", - "gix-ignore", - "gix-index", - "gix-lock", - "gix-object", - "gix-odb", - "gix-pack", + "gix-hashtable 0.8.1", + "gix-ignore 0.14.0", + "gix-index 0.39.0", + "gix-lock 17.1.0", + "gix-object 0.48.0", + "gix-odb 0.68.0", + "gix-pack 0.58.0", "gix-path", - "gix-pathspec", - "gix-protocol", - "gix-ref", - "gix-refspec", - "gix-revision", - "gix-revwalk", - "gix-sec", - "gix-shallow", - "gix-status", - "gix-submodule", - "gix-tempfile", + "gix-pathspec 0.10.0", + "gix-protocol 0.49.0", + "gix-ref 0.51.0", + "gix-refspec 0.29.0", + "gix-revision 0.33.0", + "gix-revwalk 0.19.0", + "gix-sec 0.10.12", + "gix-shallow 0.3.0", + "gix-status 0.18.0", + "gix-submodule 0.18.0", + "gix-tempfile 17.1.0", "gix-trace", - "gix-traverse", - "gix-url", + "gix-traverse 0.45.0", + "gix-url 0.30.0", "gix-utils 0.2.0", "gix-validate 0.9.4", - "gix-worktree", + "gix-worktree 0.40.0", "once_cell", "parking_lot", "signal-hook", @@ -977,6 +1008,67 @@ dependencies = [ "thiserror", ] +[[package]] +name = "gix" +version = "0.74.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5fd3a6fea165debe0e80648495f894aa2371a771e3ceb7a7dcc304f1c4344c43" +dependencies = [ + "gix-actor 0.35.6", + "gix-archive", + "gix-attributes 0.28.1", + "gix-blame", + "gix-command 0.6.3", + "gix-commitgraph 0.30.1", + "gix-config 0.47.1", + "gix-credentials", + "gix-date 0.10.7", + "gix-diff 0.54.1", + "gix-dir 0.16.0", + "gix-discover 0.42.0", + "gix-features 0.44.1", + "gix-filter 0.21.0", + "gix-fs 0.17.0", + "gix-glob 0.22.1", + "gix-hash 0.20.1", + "gix-hashtable 0.10.0", + "gix-ignore 0.17.1", + "gix-index 0.42.1", + "gix-lock 19.0.0", + "gix-mailmap", + "gix-negotiate", + "gix-object 0.51.1", + "gix-odb 0.71.1", + "gix-pack 0.61.1", + "gix-path", + "gix-pathspec 0.13.0", + "gix-prompt", + "gix-protocol 0.52.1", + "gix-ref 0.54.1", + "gix-refspec 0.32.0", + "gix-revision 0.36.1", + "gix-revwalk 0.22.0", + "gix-sec 0.12.2", + "gix-shallow 0.6.0", + "gix-status 0.21.1", + "gix-submodule 0.21.0", + "gix-tempfile 19.0.1", + "gix-trace", + "gix-transport 0.49.1", + "gix-traverse 0.48.0", + "gix-url 0.33.1", + "gix-utils 0.3.1", + "gix-validate 0.10.1", + "gix-worktree 0.43.1", + "gix-worktree-state", + "gix-worktree-stream", + "parking_lot", + "regex", + "signal-hook", + "smallvec", + "thiserror", +] + [[package]] name = "gix-actor" version = "0.34.0" @@ -984,13 +1076,41 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f438c87d4028aca4b82f82ba8d8ab1569823cfb3e5bc5fa8456a71678b2a20e7" dependencies = [ "bstr", - "gix-date", + "gix-date 0.9.4", "gix-utils 0.2.0", "itoa", "thiserror", "winnow", ] +[[package]] +name = "gix-actor" +version = "0.35.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "987a51a7e66db6ef4dc030418eb2a42af6b913a79edd8670766122d8af3ba59e" +dependencies = [ + "bstr", + "gix-date 0.10.7", + "gix-utils 0.3.1", + "itoa", + "thiserror", + "winnow", +] + +[[package]] +name = "gix-archive" +version = "0.23.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aeb76792162bf1c6d5cf5c326bcf1efbca9953f5d73bcfbe802423a93b6b1a0a" +dependencies = [ + "bstr", + "gix-date 0.10.7", + "gix-object 0.51.1", + "gix-worktree-stream", + "jiff", + "thiserror", +] + [[package]] name = "gix-attributes" version = "0.25.0" @@ -1000,7 +1120,7 @@ dependencies = [ "bstr", "gix-glob 0.19.0", "gix-path", - "gix-quote", + "gix-quote 0.5.0", "gix-trace", "kstring", "smallvec", @@ -1009,16 +1129,52 @@ dependencies = [ ] [[package]] -name = "gix-bitmap" -version = "0.2.15" +name = "gix-attributes" +version = "0.28.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e150161b8a75b5860521cb876b506879a3376d3adc857ec7a9d35e7c6a5e531" +checksum = "cc6591add69314fc43db078076a8da6f07957c65abb0b21c3e1b6a3cf50aa18d" dependencies = [ + "bstr", + "gix-glob 0.22.1", + "gix-path", + "gix-quote 0.6.1", + "gix-trace", + "kstring", + "smallvec", "thiserror", + "unicode-bom", ] [[package]] -name = "gix-chunk" +name = "gix-bitmap" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e150161b8a75b5860521cb876b506879a3376d3adc857ec7a9d35e7c6a5e531" +dependencies = [ + "thiserror", +] + +[[package]] +name = "gix-blame" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "260df64cea7bf3ab6db00e8f8cd8f1f85513d69c19fadd714422a39b8e8a8617" +dependencies = [ + "gix-commitgraph 0.30.1", + "gix-date 0.10.7", + "gix-diff 0.54.1", + "gix-hash 0.20.1", + "gix-object 0.51.1", + "gix-revwalk 0.22.0", + "gix-trace", + "gix-traverse 0.48.0", + "gix-worktree 0.43.1", + "smallvec", + "thiserror", +] + +[[package]] +name = "gix-chunk" version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5c356b3825677cb6ff579551bb8311a81821e184453cbd105e2fc5311b288eeb" @@ -1034,7 +1190,20 @@ checksum = "c0378995847773a697f8e157fe2963ecf3462fe64be05b7b3da000b3b472def8" dependencies = [ "bstr", "gix-path", - "gix-quote", + "gix-quote 0.5.0", + "gix-trace", + "shell-words", +] + +[[package]] +name = "gix-command" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "095c8367c9dc4872a7706fbc39c7f34271b88b541120a4365ff0e36366f66e62" +dependencies = [ + "bstr", + "gix-path", + "gix-quote 0.6.1", "gix-trace", "shell-words", ] @@ -1052,6 +1221,19 @@ dependencies = [ "thiserror", ] +[[package]] +name = "gix-commitgraph" +version = "0.30.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "826994ff6c01f1ff00d6a1844d7506717810a91ffed143da71e3bf39369751ef" +dependencies = [ + "bstr", + "gix-chunk", + "gix-hash 0.20.1", + "memmap2", + "thiserror", +] + [[package]] name = "gix-config" version = "0.44.0" @@ -1059,12 +1241,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c6f830bf746604940261b49abf7f655d2c19cadc9f4142ae9379e3a316e8cfa" dependencies = [ "bstr", - "gix-config-value", + "gix-config-value 0.14.12", "gix-features 0.41.1", "gix-glob 0.19.0", "gix-path", - "gix-ref", - "gix-sec", + "gix-ref 0.51.0", + "gix-sec 0.10.12", "memchr", "once_cell", "smallvec", @@ -1073,6 +1255,26 @@ dependencies = [ "winnow", ] +[[package]] +name = "gix-config" +version = "0.47.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e74f57ea99025de9207db53488be4d59cf2000f617964c1b550880524fefbc3" +dependencies = [ + "bstr", + "gix-config-value 0.15.3", + "gix-features 0.44.1", + "gix-glob 0.22.1", + "gix-path", + "gix-ref 0.54.1", + "gix-sec 0.12.2", + "memchr", + "smallvec", + "thiserror", + "unicode-bom", + "winnow", +] + [[package]] name = "gix-config-value" version = "0.14.12" @@ -1086,6 +1288,37 @@ dependencies = [ "thiserror", ] +[[package]] +name = "gix-config-value" +version = "0.15.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c489abb061c74b0c3ad790e24a606ef968cebab48ec673d6a891ece7d5aef64" +dependencies = [ + "bitflags", + "bstr", + "gix-path", + "libc", + "thiserror", +] + +[[package]] +name = "gix-credentials" +version = "0.31.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20c2f7e9cda17bd982cfd4f7b7a2486239bb5be3e0893cf4b0178b8814ea3742" +dependencies = [ + "bstr", + "gix-command 0.6.3", + "gix-config-value 0.15.3", + "gix-date 0.10.7", + "gix-path", + "gix-prompt", + "gix-sec 0.12.2", + "gix-trace", + "gix-url 0.33.1", + "thiserror", +] + [[package]] name = "gix-date" version = "0.9.4" @@ -1098,6 +1331,19 @@ dependencies = [ "thiserror", ] +[[package]] +name = "gix-date" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "661245d045aa7c16ba4244daaabd823c562c3e45f1f25b816be2c57ee09f2171" +dependencies = [ + "bstr", + "itoa", + "jiff", + "smallvec", + "thiserror", +] + [[package]] name = "gix-diff" version = "0.51.0" @@ -1105,19 +1351,43 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2c975dad2afc85e4e233f444d1efbe436c3cdcf3a07173984509c436d00a3f8" dependencies = [ "bstr", - "gix-attributes", - "gix-command", - "gix-filter", + "gix-attributes 0.25.0", + "gix-command 0.5.0", + "gix-filter 0.18.0", "gix-fs 0.14.0", "gix-hash 0.17.0", - "gix-index", - "gix-object", + "gix-index 0.39.0", + "gix-object 0.48.0", + "gix-path", + "gix-pathspec 0.10.0", + "gix-tempfile 17.1.0", + "gix-trace", + "gix-traverse 0.45.0", + "gix-worktree 0.40.0", + "imara-diff", + "thiserror", +] + +[[package]] +name = "gix-diff" +version = "0.54.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd78d9da421baca219a650d71c797706117095635d7963f21bb6fdf2410abe04" +dependencies = [ + "bstr", + "gix-attributes 0.28.1", + "gix-command 0.6.3", + "gix-filter 0.21.0", + "gix-fs 0.17.0", + "gix-hash 0.20.1", + "gix-index 0.42.1", + "gix-object 0.51.1", "gix-path", - "gix-pathspec", - "gix-tempfile", + "gix-pathspec 0.13.0", + "gix-tempfile 19.0.1", "gix-trace", - "gix-traverse", - "gix-worktree", + "gix-traverse 0.48.0", + "gix-worktree 0.43.1", "imara-diff", "thiserror", ] @@ -1129,16 +1399,36 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5879497bd3815d8277ed864ec8975290a70de5b62bb92d2d666a4cefc5d4793b" dependencies = [ "bstr", - "gix-discover", + "gix-discover 0.39.0", "gix-fs 0.14.0", - "gix-ignore", - "gix-index", - "gix-object", + "gix-ignore 0.14.0", + "gix-index 0.39.0", + "gix-object 0.48.0", "gix-path", - "gix-pathspec", + "gix-pathspec 0.10.0", "gix-trace", "gix-utils 0.2.0", - "gix-worktree", + "gix-worktree 0.40.0", + "thiserror", +] + +[[package]] +name = "gix-dir" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f99fb4dcba076453d791949bf3af977c5678a1cbd76740ec2cfe37e29431daf3" +dependencies = [ + "bstr", + "gix-discover 0.42.0", + "gix-fs 0.17.0", + "gix-ignore 0.17.1", + "gix-index 0.42.1", + "gix-object 0.51.1", + "gix-path", + "gix-pathspec 0.13.0", + "gix-trace", + "gix-utils 0.3.1", + "gix-worktree 0.43.1", "thiserror", ] @@ -1153,8 +1443,24 @@ dependencies = [ "gix-fs 0.14.0", "gix-hash 0.17.0", "gix-path", - "gix-ref", - "gix-sec", + "gix-ref 0.51.0", + "gix-sec 0.10.12", + "thiserror", +] + +[[package]] +name = "gix-discover" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d24547153810634636471af88338240e6ab0831308cd41eb6ebfffea77811c6" +dependencies = [ + "bstr", + "dunce", + "gix-fs 0.17.0", + "gix-hash 0.20.1", + "gix-path", + "gix-ref 0.54.1", + "gix-sec 0.12.2", "thiserror", ] @@ -1171,7 +1477,7 @@ dependencies = [ "gix-utils 0.2.0", "libc", "once_cell", - "prodash", + "prodash 29.0.2", "thiserror", "walkdir", ] @@ -1185,7 +1491,7 @@ dependencies = [ "gix-trace", "gix-utils 0.3.1", "libc", - "prodash", + "prodash 29.0.2", ] [[package]] @@ -1194,8 +1500,20 @@ version = "0.44.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dfa64593d1586135102307fb57fb3a9d3868b6b1f45a4da1352cce5070f8916a" dependencies = [ + "bytes", + "bytesize", + "crc32fast", + "crossbeam-channel", + "gix-path", "gix-trace", + "gix-utils 0.3.1", "libc", + "libz-rs-sys", + "once_cell", + "parking_lot", + "prodash 30.0.1", + "thiserror", + "walkdir", ] [[package]] @@ -1206,19 +1524,40 @@ checksum = "cb2b2bbffdc5cc9b2b82fc82da1b98163c9b423ac2b45348baa83a947ac9ab89" dependencies = [ "bstr", "encoding_rs", - "gix-attributes", - "gix-command", + "gix-attributes 0.25.0", + "gix-command 0.5.0", "gix-hash 0.17.0", - "gix-object", - "gix-packetline-blocking", + "gix-object 0.48.0", + "gix-packetline-blocking 0.18.3", "gix-path", - "gix-quote", + "gix-quote 0.5.0", "gix-trace", "gix-utils 0.2.0", "smallvec", "thiserror", ] +[[package]] +name = "gix-filter" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d1253452c9808da01eaaf9b1c4929b9982efec29ef0a668b3326b8046d9b8fb" +dependencies = [ + "bstr", + "encoding_rs", + "gix-attributes 0.28.1", + "gix-command 0.6.3", + "gix-hash 0.20.1", + "gix-object 0.51.1", + "gix-packetline-blocking 0.19.3", + "gix-path", + "gix-quote 0.6.1", + "gix-trace", + "gix-utils 0.3.1", + "smallvec", + "thiserror", +] + [[package]] name = "gix-fs" version = "0.14.0" @@ -1247,6 +1586,20 @@ dependencies = [ "thiserror", ] +[[package]] +name = "gix-fs" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f1ecd896258cdc5ccd94d18386d17906b8de265ad2ecf68e3bea6b007f6a28f" +dependencies = [ + "bstr", + "fastrand", + "gix-features 0.44.1", + "gix-path", + "gix-utils 0.3.1", + "thiserror", +] + [[package]] name = "gix-glob" version = "0.19.0" @@ -1295,6 +1648,18 @@ dependencies = [ "thiserror", ] +[[package]] +name = "gix-hash" +version = "0.20.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "826036a9bee95945b0be1e2394c64cd4289916c34a639818f8fd5153906985c1" +dependencies = [ + "faster-hex 0.10.0", + "gix-features 0.44.1", + "sha1-checked", + "thiserror", +] + [[package]] name = "gix-hashtable" version = "0.8.1" @@ -1306,6 +1671,17 @@ dependencies = [ "parking_lot", ] +[[package]] +name = "gix-hashtable" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a27d4a3ea9640da504a2657fef3419c517fd71f1767ad8935298bcc805edd195" +dependencies = [ + "gix-hash 0.20.1", + "hashbrown 0.16.0", + "parking_lot", +] + [[package]] name = "gix-ignore" version = "0.14.0" @@ -1319,6 +1695,19 @@ dependencies = [ "unicode-bom", ] +[[package]] +name = "gix-ignore" +version = "0.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93b6a9679a1488123b7f2929684bacfd9cd2a24f286b52203b8752cbb8d7fc49" +dependencies = [ + "bstr", + "gix-glob 0.22.1", + "gix-path", + "gix-trace", + "unicode-bom", +] + [[package]] name = "gix-index" version = "0.39.0" @@ -1333,9 +1722,9 @@ dependencies = [ "gix-features 0.41.1", "gix-fs 0.14.0", "gix-hash 0.17.0", - "gix-lock", - "gix-object", - "gix-traverse", + "gix-lock 17.1.0", + "gix-object 0.48.0", + "gix-traverse 0.45.0", "gix-utils 0.2.0", "gix-validate 0.9.4", "hashbrown 0.14.5", @@ -1347,17 +1736,84 @@ dependencies = [ "thiserror", ] +[[package]] +name = "gix-index" +version = "0.42.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31244542fb98ea4f3e964a4f8deafc2f4c77ad42bed58a1e8424bca1965fae99" +dependencies = [ + "bitflags", + "bstr", + "filetime", + "fnv", + "gix-bitmap", + "gix-features 0.44.1", + "gix-fs 0.17.0", + "gix-hash 0.20.1", + "gix-lock 19.0.0", + "gix-object 0.51.1", + "gix-traverse 0.48.0", + "gix-utils 0.3.1", + "gix-validate 0.10.1", + "hashbrown 0.16.0", + "itoa", + "libc", + "memmap2", + "rustix 1.1.2", + "smallvec", + "thiserror", +] + [[package]] name = "gix-lock" version = "17.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "570f8b034659f256366dc90f1a24924902f20acccd6a15be96d44d1269e7a796" dependencies = [ - "gix-tempfile", + "gix-tempfile 17.1.0", "gix-utils 0.3.1", "thiserror", ] +[[package]] +name = "gix-lock" +version = "19.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "729d7857429a66023bc0c29d60fa21d0d6ae8862f33c1937ba89e0f74dd5c67f" +dependencies = [ + "gix-tempfile 19.0.1", + "gix-utils 0.3.1", + "thiserror", +] + +[[package]] +name = "gix-mailmap" +version = "0.27.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce3fc0f07ce86acc94d93e5d10ef38bad322dede2622d5ff84f0799ac13b7e7d" +dependencies = [ + "bstr", + "gix-actor 0.35.6", + "gix-date 0.10.7", + "thiserror", +] + +[[package]] +name = "gix-negotiate" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89e16c96e052467d64c8f75a703b78976b33b034b9ff1f1d0c056c584319b0b8" +dependencies = [ + "bitflags", + "gix-commitgraph 0.30.1", + "gix-date 0.10.7", + "gix-hash 0.20.1", + "gix-object 0.51.1", + "gix-revwalk 0.22.0", + "smallvec", + "thiserror", +] + [[package]] name = "gix-object" version = "0.48.0" @@ -1365,11 +1821,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4943fcdae6ffc135920c9ea71e0362ed539182924ab7a85dd9dac8d89b0dd69a" dependencies = [ "bstr", - "gix-actor", - "gix-date", + "gix-actor 0.34.0", + "gix-date 0.9.4", "gix-features 0.41.1", "gix-hash 0.17.0", - "gix-hashtable", + "gix-hashtable 0.8.1", "gix-path", "gix-utils 0.2.0", "gix-validate 0.9.4", @@ -1379,6 +1835,27 @@ dependencies = [ "winnow", ] +[[package]] +name = "gix-object" +version = "0.51.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87ba1815638759c80d2318c8e98296fb396f577c2e588a3d9c13f9a5d5184051" +dependencies = [ + "bstr", + "gix-actor 0.35.6", + "gix-date 0.10.7", + "gix-features 0.44.1", + "gix-hash 0.20.1", + "gix-hashtable 0.10.0", + "gix-path", + "gix-utils 0.3.1", + "gix-validate 0.10.1", + "itoa", + "smallvec", + "thiserror", + "winnow", +] + [[package]] name = "gix-odb" version = "0.68.0" @@ -1386,15 +1863,36 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "50306d40dcc982eb6b7593103f066ea6289c7b094cb9db14f3cd2be0b9f5e610" dependencies = [ "arc-swap", - "gix-date", + "gix-date 0.9.4", "gix-features 0.41.1", "gix-fs 0.14.0", "gix-hash 0.17.0", - "gix-hashtable", - "gix-object", - "gix-pack", + "gix-hashtable 0.8.1", + "gix-object 0.48.0", + "gix-pack 0.58.0", + "gix-path", + "gix-quote 0.5.0", + "parking_lot", + "tempfile", + "thiserror", +] + +[[package]] +name = "gix-odb" +version = "0.71.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6efc6736d3ea62640efe8c1be695fb0760af63614a7356d2091208a841f1a634" +dependencies = [ + "arc-swap", + "gix-date 0.10.7", + "gix-features 0.44.1", + "gix-fs 0.17.0", + "gix-hash 0.20.1", + "gix-hashtable 0.10.0", + "gix-object 0.51.1", + "gix-pack 0.61.1", "gix-path", - "gix-quote", + "gix-quote 0.6.1", "parking_lot", "tempfile", "thiserror", @@ -1410,14 +1908,35 @@ dependencies = [ "gix-chunk", "gix-features 0.41.1", "gix-hash 0.17.0", - "gix-hashtable", - "gix-object", + "gix-hashtable 0.8.1", + "gix-object 0.48.0", "gix-path", "memmap2", "smallvec", "thiserror", ] +[[package]] +name = "gix-pack" +version = "0.61.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "719c60524be76874f4769da20d525ad2c00a0e7059943cc4f31fcb65cfb6b260" +dependencies = [ + "clru", + "gix-chunk", + "gix-features 0.44.1", + "gix-hash 0.20.1", + "gix-hashtable 0.10.0", + "gix-object 0.51.1", + "gix-path", + "gix-tempfile 19.0.1", + "memmap2", + "parking_lot", + "smallvec", + "thiserror", + "uluru", +] + [[package]] name = "gix-packetline" version = "0.18.4" @@ -1430,6 +1949,21 @@ dependencies = [ "thiserror", ] +[[package]] +name = "gix-packetline" +version = "0.19.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64286a8b5148e76ab80932e72762dd27ccf6169dd7a134b027c8a262a8262fcf" +dependencies = [ + "bstr", + "faster-hex 0.10.0", + "futures-io", + "futures-lite", + "gix-trace", + "pin-project-lite", + "thiserror", +] + [[package]] name = "gix-packetline-blocking" version = "0.18.3" @@ -1442,6 +1976,18 @@ dependencies = [ "thiserror", ] +[[package]] +name = "gix-packetline-blocking" +version = "0.19.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89c59c3ad41e68cb38547d849e9ef5ccfc0d00f282244ba1441ae856be54d001" +dependencies = [ + "bstr", + "faster-hex 0.10.0", + "gix-trace", + "thiserror", +] + [[package]] name = "gix-path" version = "0.10.21" @@ -1463,13 +2009,41 @@ checksum = "fef8422c3c9066d649074b24025125963f85232bfad32d6d16aea9453b82ec14" dependencies = [ "bitflags", "bstr", - "gix-attributes", - "gix-config-value", + "gix-attributes 0.25.0", + "gix-config-value 0.14.12", "gix-glob 0.19.0", "gix-path", "thiserror", ] +[[package]] +name = "gix-pathspec" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d05e28457dca7c65a2dbe118869aab922a5bd382b7bb10cff5354f366845c128" +dependencies = [ + "bitflags", + "bstr", + "gix-attributes 0.28.1", + "gix-config-value 0.15.3", + "gix-glob 0.22.1", + "gix-path", + "thiserror", +] + +[[package]] +name = "gix-prompt" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "868e6516dfa16fdcbc5f8c935167d085f2ae65ccd4c9476a4319579d12a69d8d" +dependencies = [ + "gix-command 0.6.3", + "gix-config-value 0.15.3", + "parking_lot", + "rustix 1.1.2", + "thiserror", +] + [[package]] name = "gix-protocol" version = "0.49.0" @@ -1477,18 +2051,47 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5678ddae1d62880bc30e2200be1b9387af3372e0e88e21f81b4e7f8367355b5a" dependencies = [ "bstr", - "gix-date", + "gix-date 0.9.4", "gix-features 0.41.1", "gix-hash 0.17.0", - "gix-ref", - "gix-shallow", - "gix-transport", + "gix-ref 0.51.0", + "gix-shallow 0.3.0", + "gix-transport 0.46.0", "gix-utils 0.2.0", "maybe-async", "thiserror", "winnow", ] +[[package]] +name = "gix-protocol" +version = "0.52.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64f19873bbf924fd077580d4ccaaaeddb67c3b3c09a8ffb61e6b4cb67e3c9302" +dependencies = [ + "async-trait", + "bstr", + "futures-io", + "futures-lite", + "gix-credentials", + "gix-date 0.10.7", + "gix-features 0.44.1", + "gix-hash 0.20.1", + "gix-lock 19.0.0", + "gix-negotiate", + "gix-object 0.51.1", + "gix-ref 0.54.1", + "gix-refspec 0.32.0", + "gix-revwalk 0.22.0", + "gix-shallow 0.6.0", + "gix-trace", + "gix-transport 0.49.1", + "gix-utils 0.3.1", + "maybe-async", + "thiserror", + "winnow", +] + [[package]] name = "gix-quote" version = "0.5.0" @@ -1500,20 +2103,31 @@ dependencies = [ "thiserror", ] +[[package]] +name = "gix-quote" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e912ec04b7b1566a85ad486db0cab6b9955e3e32bcd3c3a734542ab3af084c5b" +dependencies = [ + "bstr", + "gix-utils 0.3.1", + "thiserror", +] + [[package]] name = "gix-ref" version = "0.51.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b2e1f7eb6b7ce82d2d19961f74bd637bab3ea79b1bc7bfb23dbefc67b0415d8b" dependencies = [ - "gix-actor", + "gix-actor 0.34.0", "gix-features 0.41.1", "gix-fs 0.14.0", "gix-hash 0.17.0", - "gix-lock", - "gix-object", + "gix-lock 17.1.0", + "gix-object 0.48.0", "gix-path", - "gix-tempfile", + "gix-tempfile 17.1.0", "gix-utils 0.2.0", "gix-validate 0.9.4", "memmap2", @@ -1521,6 +2135,27 @@ dependencies = [ "winnow", ] +[[package]] +name = "gix-ref" +version = "0.54.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8881d262f28eda39c244e60ae968f4f6e56c747f65addd6f4100b25f75ed8b88" +dependencies = [ + "gix-actor 0.35.6", + "gix-features 0.44.1", + "gix-fs 0.17.0", + "gix-hash 0.20.1", + "gix-lock 19.0.0", + "gix-object 0.51.1", + "gix-path", + "gix-tempfile 19.0.1", + "gix-utils 0.3.1", + "gix-validate 0.10.1", + "memmap2", + "thiserror", + "winnow", +] + [[package]] name = "gix-refspec" version = "0.29.0" @@ -1529,12 +2164,26 @@ checksum = "1d8587b21e2264a6e8938d940c5c99662779c13a10741a5737b15fc85c252ffc" dependencies = [ "bstr", "gix-hash 0.17.0", - "gix-revision", + "gix-revision 0.33.0", "gix-validate 0.9.4", "smallvec", "thiserror", ] +[[package]] +name = "gix-refspec" +version = "0.32.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93147960f77695ba89b72019b789679278dd4dad6a0f9a4a5bf2fd07aba56912" +dependencies = [ + "bstr", + "gix-hash 0.20.1", + "gix-revision 0.36.1", + "gix-validate 0.10.1", + "smallvec", + "thiserror", +] + [[package]] name = "gix-revision" version = "0.33.0" @@ -1543,12 +2192,30 @@ checksum = "342caa4e158df3020cadf62f656307c3948fe4eacfdf67171d7212811860c3e9" dependencies = [ "bitflags", "bstr", - "gix-commitgraph", - "gix-date", + "gix-commitgraph 0.27.0", + "gix-date 0.9.4", "gix-hash 0.17.0", - "gix-hashtable", - "gix-object", - "gix-revwalk", + "gix-hashtable 0.8.1", + "gix-object 0.48.0", + "gix-revwalk 0.19.0", + "gix-trace", + "thiserror", +] + +[[package]] +name = "gix-revision" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c5267e530d8762842be7d51b48d2b134c9dec5b650ca607f735a56a4b12413" +dependencies = [ + "bitflags", + "bstr", + "gix-commitgraph 0.30.1", + "gix-date 0.10.7", + "gix-hash 0.20.1", + "gix-hashtable 0.10.0", + "gix-object 0.51.1", + "gix-revwalk 0.22.0", "gix-trace", "thiserror", ] @@ -1559,11 +2226,26 @@ version = "0.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2dc7c3d7e5cdc1ab8d35130106e4af0a4f9f9eca0c81f4312b690780e92bde0d" dependencies = [ - "gix-commitgraph", - "gix-date", + "gix-commitgraph 0.27.0", + "gix-date 0.9.4", "gix-hash 0.17.0", - "gix-hashtable", - "gix-object", + "gix-hashtable 0.8.1", + "gix-object 0.48.0", + "smallvec", + "thiserror", +] + +[[package]] +name = "gix-revwalk" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02e2de4f91d712b1f6873477f769225fe430ffce2af8c7c85721c3ff955783b3" +dependencies = [ + "gix-commitgraph 0.30.1", + "gix-date 0.10.7", + "gix-hash 0.20.1", + "gix-hashtable 0.10.0", + "gix-object 0.51.1", "smallvec", "thiserror", ] @@ -1580,6 +2262,18 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "gix-sec" +version = "0.12.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea9962ed6d9114f7f100efe038752f41283c225bb507a2888903ac593dffa6be" +dependencies = [ + "bitflags", + "gix-path", + "libc", + "windows-sys 0.61.2", +] + [[package]] name = "gix-shallow" version = "0.3.0" @@ -1588,7 +2282,19 @@ checksum = "cc0598aacfe1d52575a21c9492fee086edbb21e228ec36c819c42ab923f434c3" dependencies = [ "bstr", "gix-hash 0.17.0", - "gix-lock", + "gix-lock 17.1.0", + "thiserror", +] + +[[package]] +name = "gix-shallow" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2374692db1ee1ffa0eddcb9e86ec218f7c4cdceda800ebc5a9fdf73a8c08223" +dependencies = [ + "bstr", + "gix-hash 0.20.1", + "gix-lock 19.0.0", "thiserror", ] @@ -1600,17 +2306,40 @@ checksum = "605a6d0eb5891680c46e24b2ee7a63ef7bd39cb136dc7c7e55172960cf68b2f5" dependencies = [ "bstr", "filetime", - "gix-diff", - "gix-dir", + "gix-diff 0.51.0", + "gix-dir 0.13.0", "gix-features 0.41.1", - "gix-filter", + "gix-filter 0.18.0", "gix-fs 0.14.0", "gix-hash 0.17.0", - "gix-index", - "gix-object", + "gix-index 0.39.0", + "gix-object 0.48.0", + "gix-path", + "gix-pathspec 0.10.0", + "gix-worktree 0.40.0", + "portable-atomic", + "thiserror", +] + +[[package]] +name = "gix-status" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c64039358f66c955a471432aef0ea1eeebc7afe0e0a4be7b6b737cc19925e3b" +dependencies = [ + "bstr", + "filetime", + "gix-diff 0.54.1", + "gix-dir 0.16.0", + "gix-features 0.44.1", + "gix-filter 0.21.0", + "gix-fs 0.17.0", + "gix-hash 0.20.1", + "gix-index 0.42.1", + "gix-object 0.51.1", "gix-path", - "gix-pathspec", - "gix-worktree", + "gix-pathspec 0.13.0", + "gix-worktree 0.43.1", "portable-atomic", "thiserror", ] @@ -1622,11 +2351,26 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78c7390c2059505c365e9548016d4edc9f35749c6a9112b7b1214400bbc68da2" dependencies = [ "bstr", - "gix-config", + "gix-config 0.44.0", + "gix-path", + "gix-pathspec 0.10.0", + "gix-refspec 0.29.0", + "gix-url 0.30.0", + "thiserror", +] + +[[package]] +name = "gix-submodule" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9bacc06333b50abc4fc06204622c2dd92850de2066bb5d421ac776d2bef7ae55" +dependencies = [ + "bstr", + "gix-config 0.47.1", "gix-path", - "gix-pathspec", - "gix-refspec", - "gix-url", + "gix-pathspec 0.13.0", + "gix-refspec 0.32.0", + "gix-url 0.33.1", "thiserror", ] @@ -1646,6 +2390,21 @@ dependencies = [ "tempfile", ] +[[package]] +name = "gix-tempfile" +version = "19.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e265fc6b54e57693232a79d84038381ebfda7b1a3b1b8a9320d4d5fe6e820086" +dependencies = [ + "dashmap", + "gix-fs 0.17.0", + "libc", + "parking_lot", + "signal-hook", + "signal-hook-registry", + "tempfile", +] + [[package]] name = "gix-trace" version = "0.1.15" @@ -1659,12 +2418,32 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b3f68c2870bfca8278389d2484a7f2215b67d0b0cc5277d3c72ad72acf41787e" dependencies = [ "bstr", - "gix-command", + "gix-command 0.5.0", "gix-features 0.41.1", - "gix-packetline", - "gix-quote", - "gix-sec", - "gix-url", + "gix-packetline 0.18.4", + "gix-quote 0.5.0", + "gix-sec 0.10.12", + "gix-url 0.30.0", + "thiserror", +] + +[[package]] +name = "gix-transport" +version = "0.49.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8da4a77922accb1e26e610c7a84ef7e6b34fd07112e6a84afd68d7f3e795957" +dependencies = [ + "async-trait", + "bstr", + "futures-io", + "futures-lite", + "gix-command 0.6.3", + "gix-features 0.44.1", + "gix-packetline 0.19.3", + "gix-quote 0.6.1", + "gix-sec 0.12.2", + "gix-url 0.33.1", + "pin-project-lite", "thiserror", ] @@ -1675,12 +2454,29 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "36c0b049f8bdb61b20016694102f7b507f2e1727e83e9c5e6dad4f7d84ff7384" dependencies = [ "bitflags", - "gix-commitgraph", - "gix-date", + "gix-commitgraph 0.27.0", + "gix-date 0.9.4", "gix-hash 0.17.0", - "gix-hashtable", - "gix-object", - "gix-revwalk", + "gix-hashtable 0.8.1", + "gix-object 0.48.0", + "gix-revwalk 0.19.0", + "smallvec", + "thiserror", +] + +[[package]] +name = "gix-traverse" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "412126bade03a34f5d4125fd64878852718575b3b360eaae3b29970cb555e2a2" +dependencies = [ + "bitflags", + "gix-commitgraph 0.30.1", + "gix-date 0.10.7", + "gix-hash 0.20.1", + "gix-hashtable 0.10.0", + "gix-object 0.51.1", + "gix-revwalk 0.22.0", "smallvec", "thiserror", ] @@ -1699,6 +2495,20 @@ dependencies = [ "url", ] +[[package]] +name = "gix-url" +version = "0.33.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c79b07b48dd9285485eb10429696ddcd1bfe6fb942ec0e5efb401ae7e40238e5" +dependencies = [ + "bstr", + "gix-features 0.44.1", + "gix-path", + "percent-encoding", + "thiserror", + "url", +] + [[package]] name = "gix-utils" version = "0.2.0" @@ -1716,6 +2526,7 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "befcdbdfb1238d2854591f760a48711bed85e72d80a10e8f2f93f656746ef7c5" dependencies = [ + "bstr", "fastrand", "unicode-normalization", ] @@ -1747,18 +2558,75 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f7760dbc4b79aa274fed30adc0d41dca6b917641f26e7867c4071b1fb4dc727b" dependencies = [ "bstr", - "gix-attributes", + "gix-attributes 0.25.0", "gix-features 0.41.1", "gix-fs 0.14.0", "gix-glob 0.19.0", "gix-hash 0.17.0", - "gix-ignore", - "gix-index", - "gix-object", + "gix-ignore 0.14.0", + "gix-index 0.39.0", + "gix-object 0.48.0", "gix-path", "gix-validate 0.9.4", ] +[[package]] +name = "gix-worktree" +version = "0.43.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8df3dfc8b62b0eccc923c757b40f488abc357c85c03d798622edfc3eb5137e04" +dependencies = [ + "bstr", + "gix-attributes 0.28.1", + "gix-features 0.44.1", + "gix-fs 0.17.0", + "gix-glob 0.22.1", + "gix-hash 0.20.1", + "gix-ignore 0.17.1", + "gix-index 0.42.1", + "gix-object 0.51.1", + "gix-path", + "gix-validate 0.10.1", +] + +[[package]] +name = "gix-worktree-state" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "046efd191ff842cc22ddce61a4e8cea75ef7e3c659772de0838b2ad74b0016ef" +dependencies = [ + "bstr", + "gix-features 0.44.1", + "gix-filter 0.21.0", + "gix-fs 0.17.0", + "gix-glob 0.22.1", + "gix-hash 0.20.1", + "gix-index 0.42.1", + "gix-object 0.51.1", + "gix-path", + "gix-worktree 0.43.1", + "io-close", + "thiserror", +] + +[[package]] +name = "gix-worktree-stream" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a629188d528f5ed8abe023cdbdc4d51ef19223552cd7e2808733f96163fbf79d" +dependencies = [ + "gix-attributes 0.28.1", + "gix-features 0.44.1", + "gix-filter 0.21.0", + "gix-fs 0.17.0", + "gix-hash 0.20.1", + "gix-object 0.51.1", + "gix-path", + "gix-traverse 0.48.0", + "parking_lot", + "thiserror", +] + [[package]] name = "h2" version = "0.3.27" @@ -1822,7 +2690,7 @@ version = "0.15.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" dependencies = [ - "foldhash", + "foldhash 0.1.5", ] [[package]] @@ -1830,6 +2698,11 @@ name = "hashbrown" version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5419bdc4f6a9207fbeba6d11b604d481addf78ecd10c11ad51e76c2f6482748d" +dependencies = [ + "allocator-api2", + "equivalent", + "foldhash 0.2.0", +] [[package]] name = "heapless" @@ -1845,10 +2718,9 @@ dependencies = [ name = "hoc" version = "1.0.0" dependencies = [ - "actix-rt", - "actix-web", "anyhow", "awc", + "axum", "badgers", "bytes", "config", @@ -1856,21 +2728,23 @@ dependencies = [ "dotenvy", "futures", "git2", - "gix-glob 0.22.1", + "gix 0.74.1", + "hyper-util", + "itertools", + "jiff", "mime", "number_prefix", - "openssl-probe", "reqwest", "ructe", "serde", "serde_json", "tempfile", + "thiserror", "tokio", + "tower-http", "tracing", - "tracing-actix-web", - "tracing-bunyan-formatter", "tracing-futures", - "tracing-log 0.2.0", + "tracing-log", "tracing-subscriber", "urlencoding", "vergen-gix", @@ -1942,6 +2816,12 @@ version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" +[[package]] +name = "human_format" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c3b1f728c459d27b12448862017b96ad4767b1ec2ec5e6434e99f1577f085b8" + [[package]] name = "hyper" version = "1.7.0" @@ -1956,6 +2836,7 @@ dependencies = [ "http 1.3.1", "http-body", "httparse", + "httpdate", "itoa", "pin-project-lite", "pin-utils", @@ -1978,22 +2859,7 @@ dependencies = [ "tokio", "tokio-rustls", "tower-service", -] - -[[package]] -name = "hyper-tls" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0" -dependencies = [ - "bytes", - "http-body-util", - "hyper", - "hyper-util", - "native-tls", - "tokio", - "tokio-native-tls", - "tower-service", + "webpki-roots", ] [[package]] @@ -2002,7 +2868,7 @@ version = "0.1.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c6995591a8f1380fcb4ba966a252a4b29188d51d2b89e3a252f5305be65aea8" dependencies = [ - "base64 0.22.1", + "base64", "bytes", "futures-channel", "futures-core", @@ -2014,7 +2880,7 @@ dependencies = [ "libc", "percent-encoding", "pin-project-lite", - "socket2 0.6.1", + "socket2", "system-configuration", "tokio", "tower-service", @@ -2155,6 +3021,16 @@ dependencies = [ "hashbrown 0.16.0", ] +[[package]] +name = "io-close" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9cadcf447f06744f8ce713d2d6239bb5bde2c357a452397a9ed90c625da390bc" +dependencies = [ + "libc", + "winapi", +] + [[package]] name = "ipnet" version = "2.11.0" @@ -2171,6 +3047,15 @@ dependencies = [ "serde", ] +[[package]] +name = "itertools" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285" +dependencies = [ + "either", +] + [[package]] name = "itoa" version = "1.0.15" @@ -2179,24 +3064,24 @@ checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" [[package]] name = "jiff" -version = "0.2.15" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be1f93b8b1eb69c77f24bbb0afdf66f54b632ee39af40ca21c4365a1d7347e49" +checksum = "49cce2b81f2098e7e3efc35bc2e0a6b7abec9d34128283d7a26fa8f32a6dbb35" dependencies = [ "jiff-static", "jiff-tzdb-platform", "log", "portable-atomic", "portable-atomic-util", - "serde", - "windows-sys 0.59.0", + "serde_core", + "windows-sys 0.61.2", ] [[package]] name = "jiff-static" -version = "0.2.15" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03343451ff899767262ec32146f6d559dd759fdadf42ff0e227c7c48f72594b4" +checksum = "980af8b43c3ad5d8d349ace167ec8170839f753a42d233ba19e08afe1850fa69" dependencies = [ "proc-macro2", "quote", @@ -2304,6 +3189,15 @@ dependencies = [ "vcpkg", ] +[[package]] +name = "libz-rs-sys" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "840db8cf39d9ec4dd794376f38acc40d0fc65eec2a8f484f7fd375b84602becd" +dependencies = [ + "zlib-rs", +] + [[package]] name = "libz-sys" version = "1.1.22" @@ -2366,6 +3260,12 @@ version = "0.4.28" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432" +[[package]] +name = "lru-slab" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154" + [[package]] name = "matchers" version = "0.2.0" @@ -2375,6 +3275,12 @@ dependencies = [ "regex-automata", ] +[[package]] +name = "matchit" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47e1ffaa40ddd1f3ed91f717a33c8c0ee23fff369e3aa8772b9605cc1d22f4c3" + [[package]] name = "maybe-async" version = "0.2.10" @@ -2430,34 +3336,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "69d83b0086dc8ecf3ce9ae2874b2d1290252e2a30720bea58a5c6639b0092873" dependencies = [ "libc", - "log", "wasi", "windows-sys 0.61.2", ] -[[package]] -name = "mutually_exclusive_features" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e94e1e6445d314f972ff7395df2de295fe51b71821694f0b0e1e79c4f12c8577" - -[[package]] -name = "native-tls" -version = "0.2.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87de3442987e9dbec73158d5c715e7ad9072fda936bb03d19d7fa10e00520f0e" -dependencies = [ - "libc", - "log", - "openssl", - "openssl-probe", - "openssl-sys", - "schannel", - "security-framework", - "security-framework-sys", - "tempfile", -] - [[package]] name = "nom" version = "8.0.0" @@ -2512,32 +3394,6 @@ version = "1.21.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" -[[package]] -name = "openssl" -version = "0.10.74" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24ad14dd45412269e1a30f52ad8f0664f0f4f4a89ee8fe28c3b3527021ebb654" -dependencies = [ - "bitflags", - "cfg-if", - "foreign-types", - "libc", - "once_cell", - "openssl-macros", - "openssl-sys", -] - -[[package]] -name = "openssl-macros" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "openssl-probe" version = "0.1.6" @@ -2546,9 +3402,9 @@ checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e" [[package]] name = "openssl-sys" -version = "0.9.110" +version = "0.9.111" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a9f0075ba3c21b09f8e8b2026584b1d18d49388648f2fbbf3c97ea8deced8e2" +checksum = "82cab2d520aa75e3c58898289429321eb788c3106963d0dc886ec7a5f4adc321" dependencies = [ "cc", "libc", @@ -2565,6 +3421,12 @@ dependencies = [ "ttf-parser", ] +[[package]] +name = "parking" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba" + [[package]] name = "parking_lot" version = "0.12.5" @@ -2696,11 +3558,77 @@ dependencies = [ "parking_lot", ] +[[package]] +name = "prodash" +version = "30.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a6efc566849d3d9d737c5cb06cc50e48950ebe3d3f9d70631490fff3a07b139" +dependencies = [ + "bytesize", + "human_format", + "parking_lot", +] + +[[package]] +name = "quinn" +version = "0.11.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e20a958963c291dc322d98411f541009df2ced7b5a4f2bd52337638cfccf20" +dependencies = [ + "bytes", + "cfg_aliases", + "pin-project-lite", + "quinn-proto", + "quinn-udp", + "rustc-hash", + "rustls", + "socket2", + "thiserror", + "tokio", + "tracing", + "web-time", +] + +[[package]] +name = "quinn-proto" +version = "0.11.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1906b49b0c3bc04b5fe5d86a77925ae6524a19b816ae38ce1e426255f1d8a31" +dependencies = [ + "bytes", + "getrandom 0.3.4", + "lru-slab", + "rand", + "ring", + "rustc-hash", + "rustls", + "rustls-pki-types", + "slab", + "thiserror", + "tinyvec", + "tracing", + "web-time", +] + +[[package]] +name = "quinn-udp" +version = "0.5.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "addec6a0dcad8a8d96a771f815f0eaf55f9d1805756410b39f5fa81332574cbd" +dependencies = [ + "cfg_aliases", + "libc", + "once_cell", + "socket2", + "tracing", + "windows-sys 0.60.2", +] + [[package]] name = "quote" -version = "1.0.41" +version = "1.0.42" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce25767e7b499d1b604768e7cde645d14cc8584231ea6b295e9c9eb22c02e1d1" +checksum = "a338cc41d27e6cc6dce6cefc13a0729dfbb81c262b1f519331575dd80ef3067f" dependencies = [ "proc-macro2", ] @@ -2772,12 +3700,6 @@ dependencies = [ "regex-syntax", ] -[[package]] -name = "regex-lite" -version = "0.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d942b98df5e658f56f20d592c7f868833fe38115e65c33003d8cd224b0155da" - [[package]] name = "regex-syntax" version = "0.8.8" @@ -2790,7 +3712,7 @@ version = "0.12.24" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9d0946410b9f7b082a427e4ef5c8ff541a88b357bc6c637c40db3a68ac70a36f" dependencies = [ - "base64 0.22.1", + "base64", "bytes", "encoding_rs", "futures-core", @@ -2800,21 +3722,21 @@ dependencies = [ "http-body-util", "hyper", "hyper-rustls", - "hyper-tls", "hyper-util", "js-sys", "log", "mime", - "native-tls", "percent-encoding", "pin-project-lite", + "quinn", + "rustls", "rustls-pki-types", "serde", "serde_json", "serde_urlencoded", "sync_wrapper", "tokio", - "tokio-native-tls", + "tokio-rustls", "tower", "tower-http", "tower-service", @@ -2822,6 +3744,7 @@ dependencies = [ "wasm-bindgen", "wasm-bindgen-futures", "web-sys", + "webpki-roots", ] [[package]] @@ -2844,7 +3767,7 @@ version = "0.18.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dd62dfaca6f048b191559a301dbe2c008eeddee96c23255d0d3df7cfa6434669" dependencies = [ - "base64 0.22.1", + "base64", "bytecount", "md5", "mime", @@ -2852,6 +3775,12 @@ dependencies = [ "nom-language", ] +[[package]] +name = "rustc-hash" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" + [[package]] name = "rustix" version = "0.38.44" @@ -2885,6 +3814,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "533f54bc6a7d4f647e46ad909549eda97bf5afc1585190ef692b4286b198bd8f" dependencies = [ "once_cell", + "ring", "rustls-pki-types", "rustls-webpki", "subtle", @@ -2897,6 +3827,7 @@ version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94182ad936a0c91c324cd46c6511b9510ed16af436d7b5bab34beab0afd55f7a" dependencies = [ + "web-time", "zeroize", ] @@ -2932,44 +3863,12 @@ dependencies = [ "winapi-util", ] -[[package]] -name = "schannel" -version = "0.1.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "891d81b926048e76efe18581bf793546b4c0eaf8448d72be8de2bbee5fd166e1" -dependencies = [ - "windows-sys 0.61.2", -] - [[package]] name = "scopeguard" version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" -[[package]] -name = "security-framework" -version = "2.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" -dependencies = [ - "bitflags", - "core-foundation", - "core-foundation-sys", - "libc", - "security-framework-sys", -] - -[[package]] -name = "security-framework-sys" -version = "2.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc1f0cbffaac4852523ce30d8bd3c5cdc873501d96ff467ca09b6767bb8cd5c0" -dependencies = [ - "core-foundation-sys", - "libc", -] - [[package]] name = "serde" version = "1.0.228" @@ -3013,6 +3912,17 @@ dependencies = [ "serde_core", ] +[[package]] +name = "serde_path_to_error" +version = "0.1.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10a9ff822e371bb5403e391ecd83e182e0e77ba7f6fe0160b795797109d1b457" +dependencies = [ + "itoa", + "serde", + "serde_core", +] + [[package]] name = "serde_spanned" version = "1.0.3" @@ -3113,16 +4023,6 @@ version = "1.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" -[[package]] -name = "socket2" -version = "0.5.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e22376abed350d73dd1cd119b57ffccad95b4e585a7cda43e286245ce23c0678" -dependencies = [ - "libc", - "windows-sys 0.52.0", -] - [[package]] name = "socket2" version = "0.6.1" @@ -3321,18 +4221,20 @@ dependencies = [ "parking_lot", "pin-project-lite", "signal-hook-registry", - "socket2 0.6.1", + "socket2", + "tokio-macros", "windows-sys 0.61.2", ] [[package]] -name = "tokio-native-tls" -version = "0.3.1" +name = "tokio-macros" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" +checksum = "af407857209536a95c8e56f8231ef2c2e2aff839b22e07a1ffcbc617e9db9fa5" dependencies = [ - "native-tls", - "tokio", + "proc-macro2", + "quote", + "syn", ] [[package]] @@ -3402,6 +4304,7 @@ dependencies = [ "tokio", "tower-layer", "tower-service", + "tracing", ] [[package]] @@ -3410,16 +4313,22 @@ version = "0.6.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "adc82fd73de2a9722ac5da747f12383d2bfdb93591ee6c58486e0097890f05f2" dependencies = [ + "async-compression", "bitflags", "bytes", + "futures-core", "futures-util", "http 1.3.1", "http-body", "iri-string", "pin-project-lite", + "tokio", + "tokio-util", "tower", "tower-layer", "tower-service", + "tracing", + "uuid", ] [[package]] @@ -3446,19 +4355,6 @@ dependencies = [ "tracing-core", ] -[[package]] -name = "tracing-actix-web" -version = "0.7.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5360edd490ec8dee9fedfc6a9fd83ac2f01b3e1996e3261b9ad18a61971fe064" -dependencies = [ - "actix-web", - "mutually_exclusive_features", - "pin-project", - "tracing", - "uuid", -] - [[package]] name = "tracing-attributes" version = "0.1.30" @@ -3470,24 +4366,6 @@ dependencies = [ "syn", ] -[[package]] -name = "tracing-bunyan-formatter" -version = "0.3.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d637245a0d8774bd48df6482e086c59a8b5348a910c3b0579354045a9d82411" -dependencies = [ - "ahash", - "gethostname", - "log", - "serde", - "serde_json", - "time", - "tracing", - "tracing-core", - "tracing-log 0.1.4", - "tracing-subscriber", -] - [[package]] name = "tracing-core" version = "0.1.34" @@ -3508,17 +4386,6 @@ dependencies = [ "tracing", ] -[[package]] -name = "tracing-log" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f751112709b4e791d8ce53e32c4ed2d353565a795ce84da2285393f41557bdf2" -dependencies = [ - "log", - "once_cell", - "tracing-core", -] - [[package]] name = "tracing-log" version = "0.2.0" @@ -3545,7 +4412,7 @@ dependencies = [ "thread_local", "tracing", "tracing-core", - "tracing-log 0.2.0", + "tracing-log", ] [[package]] @@ -3566,6 +4433,15 @@ version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb" +[[package]] +name = "uluru" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c8a2469e56e6e5095c82ccd3afb98dad95f7af7929aab6d8ba8d6e0f73657da" +dependencies = [ + "arrayvec", +] + [[package]] name = "unicode-bom" version = "2.0.3" @@ -3666,7 +4542,7 @@ checksum = "5f8dfe6eb333a1397e596164ae7326f68e4b95267b41aedc6aa3c81f3426a010" dependencies = [ "anyhow", "derive_builder", - "gix", + "gix 0.71.0", "rustversion", "time", "vergen", @@ -3792,6 +4668,25 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "web-time" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "webpki-roots" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2878ef029c47c6e8cf779119f20fcf52bde7ad42a731b2a304bc221df17571e" +dependencies = [ + "rustls-pki-types", +] + [[package]] name = "winapi" version = "0.3.9" @@ -4153,6 +5048,12 @@ dependencies = [ "syn", ] +[[package]] +name = "zlib-rs" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f06ae92f42f5e5c42443fd094f245eb656abf56dd7cce9b8b263236565e00f2" + [[package]] name = "zstd" version = "0.13.3" diff --git a/Cargo.toml b/Cargo.toml index 5e141a3b..0146210b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,25 +13,41 @@ path = "src/main.rs" name = "hoc" [dependencies] -actix-rt = "2.11.0" -actix-web = "4.11.0" -badgers = "1.2.0" +anyhow = "1.0.100" +axum = { version = "0.8.6", features = ["macros"] } +badgers = "2.0.0" bytes = "1.10.1" config = { version = "0.15.18", features = ["toml"], default-features = false } dashmap = "6.1.0" dotenvy = "0.15.7" futures = "0.3.31" git2 = "0.20.2" -gix-glob = "0.22.1" +gix = { version = "0.74.1", features = ["async-network-client"] } +itertools = "0.14.0" +jiff = "0.2.15" mime = "0.3" number_prefix = "0.4.0" -openssl-probe = "0.1.6" -reqwest = "0.12.24" +reqwest = { version = "0.12.24", default-features = false, features = [ + "charset", + "system-proxy", + "http2", + "rustls-tls", +] } serde = { version = "1.0.228", features = ["derive"] } serde_json = "1.0.145" +thiserror = "2.0.17" +tokio = { version = "1.48.0", features = [ + "macros", + "rt-multi-thread", + "signal", +] } +tower-http = { version = "0.6.6", features = [ + "trace", + "request-id", + "compression-deflate", + "compression-gzip", +] } tracing = "0.1.41" -tracing-actix-web = "0.7.19" -tracing-bunyan-formatter = "0.3.10" tracing-futures = "0.2.5" tracing-log = "0.2.0" tracing-subscriber = { version = "0.3.20", features = [ @@ -47,6 +63,11 @@ vergen-gix = "1.0.9" [dev-dependencies] awc = "3.8.1" +hyper-util = { version = "0.1.17", features = [ + "client", + "http1", + "client-legacy", +] } ructe = "0.18.2" tempfile = "3.23.0" tokio = "1.48.0" diff --git a/flake.nix b/flake.nix index f45dc53d..20f7c872 100644 --- a/flake.nix +++ b/flake.nix @@ -19,8 +19,6 @@ pkgs.rust-bin.fromRustupToolchainFile ./rust-toolchain.toml; in { devShells.default = with pkgs; - mkShell { - buildInputs = [ openssl pkg-config rust-toolchain bunyan-rs ]; - }; + mkShell { buildInputs = [ rust-toolchain ]; }; }); } diff --git a/src/cache.rs b/src/cache.rs index bf619899..88f2c1ae 100644 --- a/src/cache.rs +++ b/src/cache.rs @@ -1,7 +1,7 @@ use crate::{ config::Settings, error::{Error, Result}, - service::FormValue, + platform::Platform, }; use std::{ @@ -19,7 +19,7 @@ pub(crate) trait Cache { fn load(&self, key: &K) -> Result>; fn store(&self, key: K, value: V) -> Result<()>; - fn clear(&self, service: FormValue, owner: &str, repo: &str) -> Result<()>; + fn clear(&self, platform: Platform, owner: &str, repo: &str) -> Result<()>; } pub(crate) trait ToQuery { @@ -39,7 +39,7 @@ impl ToQuery for Excludes { #[derive(Hash, Eq, PartialEq, Clone, Debug)] pub(crate) struct CacheKey { - service: FormValue, + platform: Platform, owner: String, repo: String, branch: String, @@ -48,14 +48,14 @@ pub(crate) struct CacheKey { impl CacheKey { pub(crate) fn new( - service: FormValue, + platform: Platform, owner: String, repo: String, branch: String, excludes: Excludes, ) -> Self { Self { - service, + platform, owner, repo, branch, @@ -68,7 +68,7 @@ impl CacheKey { settings .cachedir - .join(self.service.url()) + .join(self.platform.domain()) .join(self.owner.as_str()) .join(self.repo.as_str()) .join(self.branch.as_str()) @@ -96,7 +96,7 @@ impl Drop for Persist { fn drop(&mut self) { info!("persisting cache"); for r in &self.in_memory.cache { - let service = *r.key(); + let platform = *r.key(); for r in r.value() { let owner = r.key(); for r in r.value() { @@ -106,7 +106,7 @@ impl Drop for Persist { for r in r.value() { let excludes = r.key().clone(); let key = CacheKey::new( - service, + platform, owner.clone(), repo.clone(), branch.clone(), @@ -141,9 +141,9 @@ impl Cache for Persist { self.in_memory.store(key, value) } - fn clear(&self, service: FormValue, owner: &str, repo: &str) -> Result<()> { - let im_res = self.in_memory.clear(service, owner, repo); - let disk_res = self.disk.clear(service, owner, repo); + fn clear(&self, platform: Platform, owner: &str, repo: &str) -> Result<()> { + let im_res = self.in_memory.clear(platform, owner, repo); + let disk_res = self.disk.clear(platform, owner, repo); if let Err(e) = im_res { Err(e)? } else if let Err(e) = disk_res { @@ -157,7 +157,7 @@ impl Cache for Persist { struct InMemoryCache { #[allow(clippy::type_complexity)] cache: DashMap< - FormValue, + Platform, DashMap>>>, >, } @@ -173,7 +173,7 @@ impl InMemoryCache { impl Cache for InMemoryCache { fn store(&self, key: CacheKey, value: CacheEntry) -> Result<()> { self.cache - .entry(key.service) + .entry(key.platform) .or_default() .entry(key.owner) .or_default() @@ -186,7 +186,7 @@ impl Cache for InMemoryCache { } fn load(&self, key: &CacheKey) -> Result> { - Ok(self.cache.get(&key.service).and_then(|c| { + Ok(self.cache.get(&key.platform).and_then(|c| { c.get(&key.owner).and_then(|c| { c.get(&key.repo).and_then(|c| { c.get(&key.branch) @@ -196,8 +196,8 @@ impl Cache for InMemoryCache { })) } - fn clear(&self, service: FormValue, owner: &str, repo: &str) -> Result<()> { - if let Some(c) = self.cache.get(&service) + fn clear(&self, platform: Platform, owner: &str, repo: &str) -> Result<()> { + if let Some(c) = self.cache.get(&platform) && let Some(c) = c.value().get(owner) { c.value().remove(repo); @@ -244,11 +244,11 @@ impl Cache for DiskCache { Ok(()) } - fn clear(&self, service: FormValue, owner: &str, repo: &str) -> Result<()> { + fn clear(&self, platform: Platform, owner: &str, repo: &str) -> Result<()> { let cache_dir = self .settings .cachedir - .join(service.service()) + .join(platform.url_path()) .join(owner) .join(repo); remove_dir_all(cache_dir).or_else(|e| { diff --git a/src/config.rs b/src/config.rs index 5d9948e7..c25f6d08 100644 --- a/src/config.rs +++ b/src/config.rs @@ -1,7 +1,9 @@ use std::path::PathBuf; +use anyhow::Result; use config::{Config, ConfigError, Environment, File}; use serde::Deserialize; +use tokio::net::TcpListener; #[derive(Debug, Deserialize, Clone)] pub struct Settings { @@ -38,4 +40,17 @@ impl Settings { .build()? .try_deserialize() } + + /// Create a [`TcpListener`] for this config. + /// + /// # Errors + /// + /// If binding fails + pub async fn listener(&self) -> Result { + Ok(tokio::net::TcpListener::bind(self.listen_addr()).await?) + } + + fn listen_addr(&self) -> String { + format!("{}:{}", self.host, self.port) + } } diff --git a/src/count.rs b/src/count.rs index 3a21e3f5..138e3e32 100644 --- a/src/count.rs +++ b/src/count.rs @@ -9,7 +9,7 @@ use std::{ use tracing::{instrument, trace}; -/// The on disk layout for served repos is `//` +/// The on disk layout for served repos is `//` /// so to get the amount of repos, we just have to count everything /// in `*/*/*` to get the count. /// diff --git a/src/error.rs b/src/error.rs index 739b6216..90a9f54c 100644 --- a/src/error.rs +++ b/src/error.rs @@ -1,94 +1,37 @@ -use crate::{statics::VERSION_INFO, templates}; - -use std::fmt; - -use actix_web::{HttpResponse, ResponseError, http::StatusCode}; +use thiserror::Error; pub(crate) type Result = std::result::Result; -#[derive(Debug)] +#[derive(Error, Debug)] pub enum Error { - Badge(String), - Client(reqwest::Error), - Git(git2::Error), + #[error("Badge({0})")] + Badge(#[from] badgers::Error), + #[error("Client({0})")] + Client(#[from] reqwest::Error), + #[error("Git({0})")] + Git(#[from] git2::Error), + #[error("RepoInit({0})")] + RepoInit(#[from] gix::init::Error), + #[error("RemoteInit({0})")] + RemoteInit(#[from] gix::remote::init::Error), + #[error("RemoteConfig({0})")] + RemoteConfig(#[from] gix::config::file::set_raw_value::Error), + #[error("RepoConfig({0})")] + RepoConfig(#[from] gix::config::Error), + #[error("RemoteMissing({0})")] + RemoteMissing(#[from] gix::remote::find::existing::Error), + #[error("Internal")] Internal, - Io(std::io::Error), - Parse(std::num::ParseIntError), - Serial(serde_json::Error), + #[error("Io({0})")] + Io(#[from] std::io::Error), + #[error("Parse({0})")] + Parse(#[from] std::num::ParseIntError), + #[error("Serde({0})")] + Serial(#[from] serde_json::Error), + #[error("BranchNotFound")] BranchNotFound, -} - -impl fmt::Display for Error { - fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { - match self { - Error::Badge(s) => write!(fmt, "Badge({s})"), - Error::Client(e) => write!(fmt, "Client({e})"), - Error::Git(e) => write!(fmt, "Git({e})"), - Error::Internal => write!(fmt, "Internal Error"), - Error::Io(e) => write!(fmt, "Io({e})"), - Error::Parse(e) => write!(fmt, "Parse({e})"), - Error::Serial(e) => write!(fmt, "Serial({e})"), - Error::BranchNotFound => write!(fmt, "Repo doesn't have master branch"), - } - } -} - -impl ResponseError for Error { - fn status_code(&self) -> StatusCode { - match self { - Error::BranchNotFound => StatusCode::NOT_FOUND, - _ => StatusCode::INTERNAL_SERVER_ERROR, - } - } - - fn error_response(&self) -> HttpResponse { - let mut buf = Vec::new(); - if let Error::BranchNotFound = self { - templates::p404_no_master_html(&mut buf, VERSION_INFO, 0).unwrap(); - HttpResponse::NotFound().content_type("text/html").body(buf) - } else { - templates::p500_html(&mut buf, VERSION_INFO, 0).unwrap(); - HttpResponse::InternalServerError() - .content_type("text/html") - .body(buf) - } - } -} - -impl std::error::Error for Error {} - -impl From for Error { - fn from(s: String) -> Self { - Error::Badge(s) - } -} - -impl From for Error { - fn from(err: git2::Error) -> Self { - Error::Git(err) - } -} - -impl From for Error { - fn from(err: std::io::Error) -> Self { - Error::Io(err) - } -} - -impl From for Error { - fn from(err: serde_json::Error) -> Self { - Error::Serial(err) - } -} - -impl From for Error { - fn from(err: reqwest::Error) -> Self { - Error::Client(err) - } -} - -impl From for Error { - fn from(err: std::num::ParseIntError) -> Self { - Error::Parse(err) - } + #[error("UnknownPlatform({0})")] + UnknownPlatform(String), + #[error(transparent)] + Other(#[from] anyhow::Error), } diff --git a/src/hoc.rs b/src/hoc.rs index a1fd0156..f4b744f0 100644 --- a/src/hoc.rs +++ b/src/hoc.rs @@ -1,140 +1,211 @@ use crate::{ cache::{Cache, CacheEntry, CacheKey, Excludes, Persist}, - error::{Error, Result}, - service::FormValue, + error::Error, + platform::Platform, }; -use std::{path::Path, process::Command}; +use std::path::Path; -use git2::{BranchType, Repository}; -use gix_glob::{Pattern, pattern::Case, wildmatch::Mode}; +use anyhow::Result; +use gix::{ + ObjectId, Repository, Tree, + glob::{Pattern, pattern::Case, wildmatch::Mode}, + object::tree::diff::Action, + refs::PartialNameRef, +}; + +use itertools::Itertools; use tracing::{debug, trace}; +fn compile_patterns(excludes: &Excludes) -> Vec { + excludes + .iter() + .filter_map(|pattern| { + let pattern = pattern.trim(); + if pattern.ends_with('/') { + let pattern = pattern.to_string() + "*"; + Pattern::from_bytes(pattern.as_bytes()) + } else { + Pattern::from_bytes(pattern.as_bytes()) + } + }) + .collect() +} + +pub(crate) struct HocCount { + pub(crate) hoc: u64, + pub(crate) commits: u64, + pub(crate) head: String, +} + pub(crate) fn hoc( repo_dir: impl AsRef, - service: FormValue, + platform: Platform, owner: &str, repo: &str, cache: &Persist, branch: &str, excludes: Excludes, -) -> Result<(u64, String, u64)> { - let repo_dir = repo_dir.as_ref().join(service.url()).join(owner).join(repo); - - let repository = Repository::open_bare(&repo_dir)?; - // TODO: do better... - let head = repository - .find_branch(branch, BranchType::Local) - .map_err(|_| Error::BranchNotFound)? - .into_reference(); - let head = format!("{}", head.target().ok_or(Error::BranchNotFound)?); - let mut arg_commit_count = vec!["rev-list".to_string(), "--count".to_string()]; - let mut arg = vec![ - "log".to_string(), - "--pretty=tformat:".to_string(), - "--numstat".to_string(), - "--ignore-space-change".to_string(), - "--ignore-all-space".to_string(), - "--ignore-submodules".to_string(), - "--no-color".to_string(), - "--find-copies-harder".to_string(), - "-M".to_string(), - "--diff-filter=ACDM".to_string(), - ]; - +) -> Result { + let repo_dir = repo_dir + .as_ref() + .join(platform.domain()) + .join(owner) + .join(repo); let patterns = compile_patterns(&excludes); - let key = CacheKey::new(service, owner.into(), repo.into(), branch.into(), excludes); + + let r = gix::open(repo_dir)?; + let branch_ref: &PartialNameRef = branch.try_into()?; + let branch_ref = r + .try_find_reference(branch_ref)? + .ok_or_else(|| Error::BranchNotFound)?; + + let head = branch_ref.target().id().to_string(); + + let key = CacheKey::new(platform, owner.into(), repo.into(), branch.into(), excludes); let cached = cache.load(&key)?; - if let Some(cached) = cached.as_ref() { + + let kind = if let Some(cached) = cached.as_ref() { debug!("using cache"); if cached.head == head { trace!("cache up to date"); - return Ok((cached.count, head, cached.commits)); + return Ok(HocCount { + hoc: cached.count, + commits: cached.commits, + head, + }); } trace!("updating cache"); - arg.push(format!("{head}..{branch}")); - arg_commit_count.push(format!("{head}..{branch}")); + Kind::Since(&cached.head) } else { debug!("Creating cache"); - arg.push(branch.to_string()); - arg_commit_count.push(branch.to_string()); - } + Kind::Full + }; + let range = Range { branch, kind }; - arg.push("--".to_string()); - arg.push(".".to_string()); - - let output = Command::new("git") - .args(&arg) - .current_dir(&repo_dir) - .output()? - .stdout; - let output = String::from_utf8_lossy(&output); - - let output_commits = Command::new("git") - .args(&arg_commit_count) - .current_dir(&repo_dir) - .output()? - .stdout; - let output_commits = String::from_utf8_lossy(&output_commits); - - let commits: u64 = output_commits.trim().parse()?; - let count: u64 = output.lines().fold(0, |sum, line| { - let mut parts = line.split_whitespace(); - let additions = parts.next(); - let deletions = parts.next(); - let file_path = parts.next().unwrap_or_default(); - - if matches(file_path, &patterns) { - sum - } else { - let additions: u64 = additions.and_then(|s| s.parse().ok()).unwrap_or_default(); - let deletions: u64 = deletions.and_then(|s| s.parse().ok()).unwrap_or_default(); - - sum + additions + deletions - } - }); + let mut x = hoc_inner(&r, &range, &patterns)?; let cached = cached.map_or_else( || CacheEntry { head: head.clone(), - count, - commits, + count: x.hoc, + commits: x.commits, }, - |c| c.update(count, commits), + |c| c.update(x.hoc, x.commits), ); + + x.hoc = cached.count; + x.commits = cached.commits; + cache.store(key, cached)?; - Ok((count, head, commits)) + Ok(x) } -fn compile_patterns(excludes: &Excludes) -> Vec { - excludes - .iter() - .filter_map(|pattern| { - let pattern = pattern.trim(); - if pattern.ends_with('/') { - let pattern = pattern.to_string() + "*"; - Pattern::from_bytes(pattern.as_bytes()) - } else { - Pattern::from_bytes(pattern.as_bytes()) - } +fn hoc_inner(repo: &Repository, range: &Range, exclude: &[Pattern]) -> Result { + let branch_ref: &PartialNameRef = range.branch.try_into()?; + let branch_ref = repo + .try_find_reference(branch_ref)? + .ok_or_else(|| Error::BranchNotFound)?; + + let head = branch_ref.target().id().to_string(); + let boundary = match range.kind { + Kind::Full => None, + Kind::Since(since) => Some(ObjectId::from_hex(since.as_bytes())?), + }; + + let mut stop = false; + let walk = repo.rev_walk([branch_ref.id()]); + let history: Vec<_> = walk + .all()? + .filter_map(Result::ok) + .take_while(|info| { + let res = stop; + stop = Some(info.id) == boundary; + !res }) - .collect() -} + .collect(); + let commits = u64::try_from(history.len())? + - if let Kind::Since(_) = range.kind { + 1 + } else { + 0 + }; -fn matches(file_path: &str, patterns: &[Pattern]) -> bool { - if file_path.is_empty() { + let insert_empty = if let Some(info) = &history.last() + && let Kind::Since(since) = range.kind + && info.id == ObjectId::from_hex(since.as_bytes())? + { false } else { - let file_path = file_path.into(); - patterns.iter().any(|pattern| { - pattern.matches_repo_relative_path( - file_path, - None, - None, - Case::Sensitive, - Mode::empty(), - ) - }) + true + }; + + let history = std::iter::chain( + history + .into_iter() + .map(|i| i.object()) + .filter_map(Result::ok) + .map(|o| o.tree()) + .filter_map(Result::ok), + if insert_empty { + Some(empty_tree(repo)) + } else { + None + }, + ); + + let hoc = history.tuple_windows().fold(0, |sum, (next, prev)| { + (if let Ok(mut changes) = prev.changes() + && let Ok(mut resource_cache) = prev.repo.diff_resource_cache_for_tree_diff() + { + let mut x = 0; + changes + .for_each_to_obtain_tree(&next, |change| { + if !exclude.iter().any(|p| { + p.matches_repo_relative_path( + change.location(), + None, + None, + Case::Sensitive, + Mode::empty(), + ) + }) && let Some(counts) = change + .diff(&mut resource_cache) + .ok() + .and_then(|mut platform| platform.line_counts().ok()) + .flatten() + { + x += u64::from(counts.insertions) + u64::from(counts.removals); + } + + resource_cache.clear_resource_cache_keep_allocation(); + Ok::<_, std::convert::Infallible>(Action::Continue) + }) + // TODO: + .unwrap(); + x + } else { + 0 + }) + sum + }); + Ok(HocCount { hoc, commits, head }) +} + +fn empty_tree(repo: &Repository) -> Tree<'_> { + Tree { + repo, + id: ObjectId::Sha1([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]), + data: Vec::new(), } } + +struct Range<'a> { + branch: &'a str, + kind: Kind<'a>, +} + +enum Kind<'a> { + Full, + Since(&'a str), +} diff --git a/src/http/hoc.rs b/src/http/hoc.rs index 5b5e78c1..da4c1346 100644 --- a/src/http/hoc.rs +++ b/src/http/hoc.rs @@ -1,9 +1,9 @@ use crate::{ - State, - cache::{Cache, Excludes, Persist, ToQuery}, + cache::{Cache, Excludes, ToQuery}, error::Result, - hoc, http, - service::Service, + hoc::{self, HocCount}, + http::{self, AppState}, + platform::Platform, statics::{CLIENT, VERSION_INFO}, template::RepoInfo, templates, @@ -14,20 +14,27 @@ use std::{ fs::create_dir_all, io, path::Path, - sync::atomic::{AtomicUsize, Ordering}, - time::{Duration, SystemTime}, + sync::{Arc, atomic::Ordering}, }; -use actix_web::{ - HttpResponse, Responder, - http::header::{CacheControl, CacheDirective, Expires, LOCATION}, - web, +use axum::{ + Json, + extract::{Path as ReqPath, Query, State}, + http::{ + StatusCode, + header::{self, HeaderMap, HeaderValue}, + }, + response::{IntoResponse, Redirect}, }; use badgers::{Badge, BadgeOptions}; -use git2::Repository; +use gix::{ + Repository, + remote::{Direction, fetch::Tags}, +}; +use jiff::{SignedDuration, Timestamp, fmt::rfc2822}; use number_prefix::NumberPrefix; use serde::{Deserialize, Serialize}; -use tracing::{Instrument, info, info_span, warn}; +use tracing::{Instrument, error, info, info_span, warn}; #[derive(Serialize)] struct JsonResponse<'a> { @@ -59,10 +66,21 @@ fn default_label() -> String { "Hits-of-Code".to_string() } -fn pull(path: impl AsRef, branch: &str) -> Result<()> { - let repo = Repository::open_bare(path)?; - let mut origin = repo.find_remote("origin")?; - origin.fetch(&[branch], None, None)?; +// TODO: pull/fetch/clone using gix +fn fetch(repo: &Repository, branch: &str) -> Result<()> { + // let repo = Repository::open_bare(path)?; + let refspec = format!("refs/heads/{branch}:refs/remotes/origin/{branch}"); + let mut remote = repo + .find_remote("origin")? + .with_fetch_tags(Tags::None) + // .with_refspecs(&[refspec.into()], Direction::Fetch) + // TODO + // .unwrap(); + ; + let url = remote.url(Direction::Fetch).unwrap(); + let mut transport = gix::tr + // remote.connect(Direction::Fetch); + // origin.fetch(&[branch], None, None)?; Ok(()) } @@ -84,31 +102,19 @@ enum HocResult { NotFound, } -pub(crate) async fn delete_repo_and_cache( - state: web::Data, - cache: web::Data, - repo_count: web::Data, - data: web::Path<(String, String)>, - branch: web::Query, -) -> Result -where - T: Service, -{ - let data = data.into_inner(); - let cache = cache.into_inner(); +pub(crate) async fn delete_repo_and_cache( + State(state): State>, + ReqPath((platform, owner, repo)): ReqPath<(Platform, String, String)>, + Query(branch): Query, +) -> Result { let span = info_span!( "deleting repository and cache", - service = T::domain(), - user = data.0.as_str(), - repo = data.1.as_str() + platform = platform.domain(), + user = owner, + repo ); let future = async { - let repo = format!( - "{}/{}/{}", - T::domain(), - data.0.to_lowercase(), - data.1.to_lowercase() - ); + let repo = format!("{}/{owner}/{repo}", platform.domain()); info!("Deleting cache and repository"); let repo_dir = state.repos().join(&repo); std::fs::remove_dir_all(repo_dir).or_else(|e| { @@ -118,9 +124,9 @@ where Err(e) } })?; - repo_count.fetch_sub(1, Ordering::Relaxed); + state.repo_count.fetch_sub(1, Ordering::Relaxed); - cache.clear(T::form_value(), data.0.as_str(), data.1.as_str())?; + state.cache.clear(platform, &owner, &repo)?; let branch_query = branch.branch.as_ref().map(|b| format!("branch={b}")); @@ -143,41 +149,34 @@ where } else { format!("?{query}") }; - Ok(HttpResponse::TemporaryRedirect() - .insert_header(( - LOCATION, - format!("/{}/{}/{}/view{query}", T::url_path(), data.0, data.1), - )) - .finish()) + Ok(Redirect::temporary(&format!( + "{}/{}/{owner}/{repo}/view{query}", + state.settings.base_url, + platform.url_path() + ))) }; future.instrument(span).await } -async fn handle_hoc_request( - state: web::Data, - cache: web::Data, - repo_count: &AtomicUsize, - data: web::Path<(String, String)>, +async fn handle_hoc_request( + state: &AppState, + (platform, owner, repo): (Platform, String, String), excludes: BTreeSet, branch: &str, -) -> Result -where - T: Service, -{ - let (owner, repo) = data.into_inner(); +) -> Result { let span = info_span!( "handling hoc calculation", - service = T::domain(), + platform = platform.domain(), user = owner.as_str(), repo = repo.as_str(), branch ); let future = async move { let slug = format!("{}/{}", owner.to_lowercase(), repo.to_lowercase()); - let service_path = format!("{}/{slug}", T::url_path()); - let service_url = format!("{}/{slug}", T::domain()); + let service_path = format!("{}/{slug}", platform.url_path()); + let service_url = format!("{}/{slug}", platform.domain()); let path = state.repos().join(&service_url); - let url = format!("https://{service_url}"); + let url = format!("https://{}/{slug}", platform.domain()); let remote_exists = remote_exists(&url).await?; let file = Path::new(&path); if !file.exists() { @@ -187,19 +186,26 @@ where } info!("Cloning for the first time"); create_dir_all(file)?; - let repo = Repository::init_bare(file)?; - repo.remote_add_fetch("origin", "refs/heads/*:refs/heads/*")?; - repo.remote_set_url("origin", &url)?; - repo_count.fetch_add(1, Ordering::Relaxed); + let mut repo = gix::init_bare(file)?; + { + let mut config = repo.config_snapshot_mut(); + config.set_raw_value(&"remote.origin.url", url.as_str())?; + config.set_raw_value( + &"remote.origin.fetch", + "+refs/heads/*:refs/remotes/origin/*", + )?; + config.commit()?; + } + state.repo_count.fetch_add(1, Ordering::Relaxed); } - pull(&path, branch)?; + fetch(&path, branch)?; - let (hoc, head, commits) = hoc::hoc( + let HocCount { hoc, head, commits } = hoc::hoc( state.repos(), - T::form_value(), + platform, &owner, repo.as_str(), - &cache, + &state.cache, branch, excludes, )?; @@ -223,60 +229,79 @@ where future.instrument(span).await } -pub(crate) async fn json_hoc( - state: web::Data, - cache: web::Data, - repo_count: web::Data, - data: web::Path<(String, String)>, - query: web::Query, -) -> Result { - let query = query.into_inner(); +pub(crate) async fn json_hoc( + State(state): State>, + ReqPath(data): ReqPath<(Platform, String, String)>, + Query(query): Query, +) -> Result { let branch = query.branch.as_deref().unwrap_or("master"); let exclude = query.excludes(); - let repo_count = repo_count.into_inner(); - let r = handle_hoc_request::(state, cache, &repo_count, data, exclude, branch).await?; + let r = handle_hoc_request(&state, data, exclude, branch).await?; match r { - HocResult::NotFound => http::p404(&repo_count), + HocResult::NotFound => Ok(http::routes::p404(State(state)).await.into_response()), HocResult::Hoc { hoc, head, commits, .. - } => Ok(HttpResponse::Ok().json(JsonResponse { + } => Ok(Json(JsonResponse { branch, head: &head, count: hoc, commits, - })), + }) + .into_response()), } } -fn no_cache_response(body: Vec) -> HttpResponse { - let expiration = SystemTime::now() + Duration::from_secs(30); - HttpResponse::Ok() - .content_type("image/svg+xml") - .insert_header(Expires(expiration.into())) - .insert_header(CacheControl(vec![ - CacheDirective::MaxAge(0u32), - CacheDirective::MustRevalidate, - CacheDirective::NoCache, - CacheDirective::NoStore, - ])) - .body(body) +fn no_cache_headers(expires: &Timestamp) -> HeaderMap { + const FORMATTER: rfc2822::DateTimePrinter = rfc2822::DateTimePrinter::new(); + let mut headers = HeaderMap::new(); + headers.insert( + header::CONTENT_TYPE, + HeaderValue::from_static("image/svg+xml"), + ); + let expires = FORMATTER.timestamp_to_rfc9110_string(expires); + if let Err(err) = &expires { + error!(%err, "formatting error"); + } + // TODO: error handling + let expires = expires.unwrap(); + info!(expires, "expires"); + + let expires_value = expires.try_into(); + if let Err(err) = &expires_value { + error!(%err, "header value error"); + } + headers.insert( + header::EXPIRES, + expires_value + // TODO: error handling + .unwrap(), + ); + headers.append(header::CACHE_CONTROL, HeaderValue::from_static("no-cache")); + headers.append(header::CACHE_CONTROL, HeaderValue::from_static("no-store")); + headers.append( + header::CACHE_CONTROL, + HeaderValue::from_static("must-revalidate"), + ); + headers.append(header::CACHE_CONTROL, HeaderValue::from_static("max-age=0")); + headers } -pub(crate) async fn calculate_hoc( - state: web::Data, - cache: web::Data, - repo_count: web::Data, - data: web::Path<(String, String)>, - query: web::Query, -) -> Result { - let query = query.into_inner(); - let repo_count = repo_count.into_inner(); +fn no_cache_response(body: Vec) -> impl IntoResponse { + let expiration = Timestamp::now() + SignedDuration::from_secs(30); + (StatusCode::OK, no_cache_headers(&expiration), body) +} + +pub(crate) async fn calculate_hoc( + State(state): State>, + ReqPath(data): ReqPath<(Platform, String, String)>, + Query(query): Query, +) -> Result { let label = query.label.clone(); let branch = query.branch.as_deref().unwrap_or("master"); let exclude = query.excludes(); - if let Ok(r) = handle_hoc_request::(state, cache, &repo_count, data, exclude, branch).await { + if let Ok(r) = handle_hoc_request(&state, data, exclude, branch).await { match r { - HocResult::NotFound => http::p404(&repo_count), + HocResult::NotFound => Ok(http::routes::p404(State(state)).await.into_response()), HocResult::Hoc { hoc_pretty, .. } => { let badge_opt = BadgeOptions { subject: label, @@ -287,7 +312,7 @@ pub(crate) async fn calculate_hoc( // TODO: remove clone let body = badge.to_svg().as_bytes().to_vec(); - Ok(no_cache_response(body)) + Ok(no_cache_response(body).into_response()) } } } else { @@ -298,26 +323,22 @@ pub(crate) async fn calculate_hoc( }) .unwrap(); let body = error_badge.to_svg().as_bytes().to_vec(); - Ok(no_cache_response(body)) + Ok(no_cache_response(body).into_response()) } } -pub(crate) async fn overview( - state: web::Data, - cache: web::Data, - repo_count: web::Data, - data: web::Path<(String, String)>, - query: web::Query, -) -> Result { - let query = query.into_inner(); +pub(crate) async fn overview( + State(state): State>, + ReqPath(data @ (platform, _, _)): ReqPath<(Platform, String, String)>, + Query(query): Query, +) -> Result { let branch = query.branch.as_deref().unwrap_or("master"); - let repo_count = repo_count.into_inner(); let label = query.label.clone(); let base_url = state.settings.base_url.clone(); let exclude = query.excludes(); - let r = handle_hoc_request::(state, cache, &repo_count, data, exclude, branch).await?; + let r = handle_hoc_request(&state, data, exclude, branch).await?; match r { - HocResult::NotFound => http::p404(&repo_count), + HocResult::NotFound => Ok(http::routes::p404(State(state)).await.into_response()), HocResult::Hoc { hoc, commits, @@ -327,9 +348,8 @@ pub(crate) async fn overview( repo, service_path, } => { - let mut buf = Vec::new(); let repo_info = RepoInfo { - commit_url: &T::commit_url(&repo, &head), + commit_url: &platform.commit_url(&repo, &head), commits, base_url: &base_url, head: &head, @@ -339,16 +359,15 @@ pub(crate) async fn overview( url: &url, branch, }; - templates::overview_html( - &mut buf, + Ok(render!( + templates::overview_html, VERSION_INFO, - repo_count.load(Ordering::Relaxed), + state.repo_count.load(Ordering::Relaxed), repo_info, &label, &query.exclude, - )?; - - Ok(HttpResponse::Ok().content_type("text/html").body(buf)) + ) + .into_response()) } } } diff --git a/src/http/mod.rs b/src/http/mod.rs index c7e1c0e9..226c212b 100644 --- a/src/http/mod.rs +++ b/src/http/mod.rs @@ -1,7 +1,118 @@ +#[macro_use] +mod render; + mod hoc; mod routes; -pub(crate) use self::{ - hoc::{calculate_hoc, delete_repo_and_cache, json_hoc, overview}, - routes::{favicon32, generate, health_check, index, p404, static_file}, +use crate::{ + cache::Persist, config::Settings, error::Error, platform::Platform, statics::VERSION_INFO, + templates, +}; + +use std::sync::{Arc, atomic::AtomicUsize}; + +use axum::{ + Router, + body::Body, + extract::{Path, Request, State}, + http::StatusCode, + response::{IntoResponse, Redirect, Response}, + routing::{get, post}, +}; +use tower_http::{ + compression::CompressionLayer, + request_id::{MakeRequestUuid, PropagateRequestIdLayer, RequestId, SetRequestIdLayer}, + trace::{DefaultMakeSpan, MakeSpan, TraceLayer}, }; +use tracing::error; + +// #[derive(Debug)] +struct AppState { + settings: Settings, + repo_count: AtomicUsize, + cache: Persist, +} + +impl AppState { + fn repos(&self) -> &std::path::Path { + &self.settings.repodir + } +} + +async fn redirect_old_overview( + State(state): State>, + Path((platform, owner, repo)): Path<(Platform, String, String)>, +) -> impl IntoResponse { + Redirect::permanent(&format!( + "{}/{}/{owner}/{repo}/view", + state.settings.base_url, + platform.url_path() + )) +} + +pub fn router(settings: Settings) -> Router { + let state = Arc::new(AppState { + settings: settings.clone(), + repo_count: AtomicUsize::new(0), + cache: Persist::new(settings), + }); + Router::new() + .route("/", get(routes::index)) + .route("/health", get(routes::health_check)) + .route("/favicon.ico", get(routes::favicon32)) + .route("/generate", post(routes::generate)) + .route("/static/{filename}", get(routes::static_file)) + .route("/view/{platform}/{user}/{repo}", get(redirect_old_overview)) + .nest( + "/{platform}/{user}/{repo}", + Router::new() + .route("/", get(hoc::calculate_hoc)) + .route("/json", get(hoc::json_hoc)) + .route("/view", get(hoc::overview)) + .route("/delete", post(hoc::delete_repo_and_cache)), + ) + .fallback(routes::p404) + .layer( + TraceLayer::new_for_http() + // add request-id to trace span + .make_span_with(|request: &Request| { + let default_span = DefaultMakeSpan::default().make_span(request); + let requestid = if let Some(req_id) = request + .extensions() + .get::() + .map(RequestId::header_value) + { + req_id.to_str().unwrap_or("") + } else { + error!("cannot extract request-id"); + "" + } + .to_string(); + tracing::info_span!(parent: &default_span, env!("CARGO_CRATE_NAME"), %requestid) + }), + ) + // PropagateRequestIdLayer must be before SetRequestIdLayer + .layer(PropagateRequestIdLayer::x_request_id()) + .layer(SetRequestIdLayer::x_request_id(MakeRequestUuid)) + .layer(CompressionLayer::new().gzip(true).deflate(true)) + .with_state(state) +} + +impl IntoResponse for Error { + fn into_response(self) -> Response { + if matches!(self, Self::BranchNotFound) || matches!(self, Self::UnknownPlatform(_)) { + ( + StatusCode::NOT_FOUND, + render!(templates::p404_no_master_html, VERSION_INFO, 0), + ) + .into_response() + } else { + error!(err=%self, ""); + ( + StatusCode::INTERNAL_SERVER_ERROR, + render!(templates::p500_html, VERSION_INFO, 0), + ) + .into_response() + } + } +} diff --git a/src/http/render.rs b/src/http/render.rs new file mode 100644 index 00000000..a7898d48 --- /dev/null +++ b/src/http/render.rs @@ -0,0 +1,31 @@ +use axum::{ + http::StatusCode, + response::{Html, IntoResponse}, +}; +use tracing::error; + +macro_rules! render { + ($template:path) => {{ + use $crate::http::render::Render; + Render(|o| $template(o)) + }}; + ($template:path, $($arg:expr),* $(,)*) => {{ + use $crate::http::render::Render; + Render(move |o| $template(o, $($arg),*)) + }} +} + +pub(crate) struct Render) -> std::io::Result<()>>(pub T); + +impl) -> std::io::Result<()>> IntoResponse for Render { + fn into_response(self) -> axum::response::Response { + let mut buf = Vec::new(); + match self.0(&mut buf) { + Ok(()) => Html(buf).into_response(), + Err(err) => { + error!(%err, "error rendering template"); + (StatusCode::INTERNAL_SERVER_ERROR, "Render failed").into_response() + } + } + } +} diff --git a/src/http/routes.rs b/src/http/routes.rs index b4fae6f9..d2a1848d 100644 --- a/src/http/routes.rs +++ b/src/http/routes.rs @@ -1,7 +1,6 @@ use crate::{ - State, - error::Result, - service::FormValue, + http::AppState, + platform::Platform, statics::VERSION_INFO, template::RepoGeneratorInfo, templates::{self, statics::StaticFile}, @@ -9,103 +8,106 @@ use crate::{ use std::{ borrow::Cow, - sync::atomic::{AtomicUsize, Ordering}, - time::{Duration, SystemTime}, + sync::{Arc, atomic::Ordering}, }; -use actix_web::{HttpResponse, get, http::header::Expires, post, web}; +use axum::{ + Form, + extract::{Path, State}, + http::{StatusCode, header}, + response::IntoResponse, +}; +use jiff::{SignedDuration, Timestamp, fmt::rfc2822}; use serde::{Deserialize, Serialize}; +use tracing::error; -#[get("/")] -#[allow(clippy::unused_async)] -pub(crate) async fn index( - state: web::Data, - repo_count: web::Data, -) -> Result { - let mut buf = Vec::new(); - templates::index_html( - &mut buf, +pub(crate) async fn index(State(state): State>) -> impl IntoResponse { + render!( + templates::index_html, VERSION_INFO, - repo_count.load(Ordering::Relaxed), + state.repo_count.load(Ordering::Relaxed), &state.settings.base_url, - )?; - Ok(HttpResponse::Ok().content_type("text/html").body(buf)) + ) } #[derive(Deserialize, Serialize)] -struct GeneratorForm<'a> { - service: FormValue, +pub(crate) struct GeneratorForm<'a> { + service: Platform, user: Cow<'a, str>, repo: Cow<'a, str>, branch: Option>, } -#[post("/generate")] -#[allow(clippy::unused_async)] -async fn generate( - params: web::Form>, - state: web::Data, - repo_count: web::Data, -) -> Result { - let mut buf = Vec::new(); - let repo_info = RepoGeneratorInfo { - service: params.service, - user: ¶ms.user, - repo: ¶ms.repo, - branch: params - .branch - .as_deref() - .filter(|s| !s.is_empty()) - .unwrap_or("master"), - }; - templates::generate_html( - &mut buf, +pub(crate) async fn generate( + State(state): State>, + Form(params): Form>, +) -> impl IntoResponse { + render!( + templates::generate_html, VERSION_INFO, - repo_count.load(Ordering::Relaxed), + state.repo_count.load(Ordering::Relaxed), &state.settings.base_url, - &repo_info, - )?; - - Ok(HttpResponse::Ok().content_type("text/html").body(buf)) + &RepoGeneratorInfo { + platform: params.service, + user: ¶ms.user, + repo: ¶ms.repo, + branch: params + .branch + .as_deref() + .filter(|s| !s.is_empty()) + .unwrap_or("master"), + } + ) } -#[get("/static/{filename}")] -#[allow(clippy::unused_async)] -async fn static_file( - path: web::Path, - repo_count: web::Data, -) -> Result { +pub(crate) async fn static_file( + Path(path): Path, + State(state): State>, +) -> impl IntoResponse { /// A duration to add to current time for a far expires header. - static FAR: Duration = Duration::from_secs(180 * 24 * 60 * 60); + const FAR: SignedDuration = SignedDuration::from_hours(180 * 24); - StaticFile::get(&path) - .map(|data| { - let far_expires = SystemTime::now() + FAR; - HttpResponse::Ok() - .insert_header(Expires(far_expires.into())) - .content_type(data.mime.clone()) - .body(data.content) - }) - .map_or_else(|| p404(&repo_count), Result::Ok) + if let Some(data) = StaticFile::get(&path) { + let far_expires = Timestamp::now() + FAR; + let formatter = rfc2822::DateTimePrinter::new(); + let expires = formatter.timestamp_to_string(&far_expires); + if let Err(err) = &expires { + error!(%err, "formatter error"); + } + ( + StatusCode::OK, + [ + ( + header::EXPIRES, + // TODO: error handling + expires.unwrap(), + ), + (header::CONTENT_TYPE, data.mime.to_string()), + ], + data.content, + ) + .into_response() + } else { + p404(State(state)).await.into_response() + } } -#[get("/favicon.ico")] -#[allow(clippy::unused_async)] -async fn favicon32() -> HttpResponse { +pub(crate) async fn favicon32() -> impl IntoResponse { let data = &crate::templates::statics::favicon32_png; - HttpResponse::Ok() - .content_type(data.mime.clone()) - .body(data.content) + ( + [(header::CONTENT_TYPE, data.mime.to_string())], + data.content, + ) } -#[get("/health_check")] -#[allow(clippy::unused_async)] -async fn health_check() -> HttpResponse { - HttpResponse::Ok().finish() +pub(crate) async fn health_check() -> impl IntoResponse { + StatusCode::OK } -pub(crate) fn p404(repo_count: &AtomicUsize) -> Result { - let mut buf = Vec::new(); - templates::p404_html(&mut buf, VERSION_INFO, repo_count.load(Ordering::Relaxed))?; - Ok(HttpResponse::NotFound().content_type("text/html").body(buf)) +pub(crate) async fn p404(State(state): State>) -> impl IntoResponse { + render!( + templates::p404_html, + VERSION_INFO, + state.repo_count.load(Ordering::Relaxed) + ) } diff --git a/src/lib.rs b/src/lib.rs index 40230260..fe67ec27 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -3,74 +3,23 @@ pub mod config; pub mod count; mod error; mod hoc; -mod http; -mod service; +pub mod http; +mod platform; mod statics; pub mod telemetry; mod template; -use crate::{ - cache::Persist, - config::Settings, - service::{Bitbucket, GitHub, Gitlab, Service, Sourcehut}, -}; +use crate::config::Settings; -use std::{net::TcpListener, path::Path, sync::atomic::AtomicUsize}; - -use actix_web::{ - App, HttpServer, - dev::Server, - middleware::{self, TrailingSlash}, - web, -}; -use tracing::{Instrument, info_span}; +use tokio::{net::TcpListener, signal}; +use tracing::{Instrument, info, info_span}; include!(concat!(env!("OUT_DIR"), "/templates.rs")); -#[derive(Debug)] -pub(crate) struct State { - settings: Settings, -} - -impl State { - fn repos(&self) -> &Path { - &self.settings.repodir - } -} - -#[allow(clippy::unused_async)] -async fn start_server(listener: TcpListener, settings: Settings) -> std::io::Result { - let workers = settings.workers; - let repo_count = - // TODO: errorhandling - web::Data::new(AtomicUsize::new(count::count_repositories(&settings.repodir).unwrap())); - let state = web::Data::new(State { - settings: settings.clone(), - }); - let cache = web::Data::new(Persist::new(settings)); - Ok(HttpServer::new(move || { - let app = App::new() - .app_data(state.clone()) - .app_data(repo_count.clone()) - .app_data(cache.clone()) - .wrap(tracing_actix_web::TracingLogger::default()) - .wrap(middleware::NormalizePath::new(TrailingSlash::Trim)) - .service(http::index) - .service(http::health_check) - .service(http::static_file) - .service(http::favicon32) - .service(http::generate) - .default_service(web::to(|repo_count: web::Data| async move { - http::p404(&repo_count) - })); - let app = GitHub::register_service(app); - let app = Gitlab::register_service(app); - let app = Bitbucket::register_service(app); - Sourcehut::register_service(app) - }) - .workers(workers) - .listen(listener)? - .run()) +async fn start_server(listener: TcpListener, settings: Settings) -> std::io::Result<()> { + axum::serve(listener, http::router(settings)) + .with_graceful_shutdown(shutdown_signal()) + .await } /// Start the server. @@ -78,8 +27,34 @@ async fn start_server(listener: TcpListener, settings: Settings) -> std::io::Res /// # Errors /// /// * server cannot bind to `listener` -pub async fn run(listener: TcpListener, settings: Settings) -> std::io::Result { +pub async fn run(listener: TcpListener, settings: Settings) -> std::io::Result<()> { let span = info_span!("hoc", version = env!("CARGO_PKG_VERSION")); let _ = span.enter(); start_server(listener, settings).instrument(span).await } + +async fn shutdown_signal() { + let ctrl_c = async { + signal::ctrl_c() + .await + .expect("failed to install Ctrl+C handler"); + }; + + #[cfg(unix)] + let terminate = async { + signal::unix::signal(signal::unix::SignalKind::terminate()) + .expect("failed to install signal handler") + .recv() + .await; + }; + + #[cfg(not(unix))] + let terminate = std::future::pending::<()>(); + + tokio::select! { + () = ctrl_c => {}, + () = terminate => {}, + } + + info!("signal received, starting graceful shutdown"); +} diff --git a/src/main.rs b/src/main.rs index 89f42012..8fb262e9 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,26 +1,21 @@ use hoc::{config::Settings, telemetry}; -use std::net::TcpListener; +use anyhow::Result; fn init() { dotenvy::dotenv().ok(); - openssl_probe::init_ssl_cert_env_vars(); - telemetry::init_subscriber(telemetry::get_subscriber("hoc", "info")); + telemetry::init_subscriber(telemetry::get_subscriber("info")); } -#[actix_rt::main] -async fn main() -> std::io::Result<()> { +#[tokio::main] +async fn main() -> Result<()> { init(); - // TODO: error handling - let settings = Settings::load().expect("Cannot load config"); + let settings = Settings::load()?; - let address = format!("{}:{}", settings.host, settings.port); - // TODO: error handling - let listener = TcpListener::bind(address)?; - hoc::run(listener, settings) - .await - .expect("Server error") - .await + let listener = settings.listener().await?; + + hoc::run(listener, settings).await?; + Ok(()) } diff --git a/src/platform.rs b/src/platform.rs new file mode 100644 index 00000000..c3a9abcd --- /dev/null +++ b/src/platform.rs @@ -0,0 +1,62 @@ +use crate::error::Error; + +use std::str::FromStr; + +use serde::{Deserialize, Serialize}; + +#[derive(Deserialize, Serialize, Clone, Copy, Hash, Eq, PartialEq, Debug)] +pub enum Platform { + #[serde(rename = "github")] + GitHub, + #[serde(rename = "gitlab")] + Gitlab, + #[serde(rename = "bitbucket")] + Bitbucket, + #[serde(rename = "sourcehut")] + Sourcehut, +} + +impl Platform { + pub(crate) fn domain(self) -> &'static str { + match self { + Self::GitHub => "github.com", + Self::Gitlab => "gitlab.com", + Self::Bitbucket => "bitbucket.org", + Self::Sourcehut => "git.sr.ht", + } + } + + pub(crate) fn url_path(self) -> &'static str { + match self { + Self::GitHub => "github", + Self::Gitlab => "gitlab", + Self::Bitbucket => "bitbucket", + Self::Sourcehut => "sourcehut", + } + } + + pub(crate) fn commit_url(self, repo: &str, commit_ref: &str) -> String { + match self { + Self::GitHub | Self::Gitlab | Self::Sourcehut => { + format!("https://{}/{repo}/commit/{commit_ref}", self.domain()) + } + Self::Bitbucket => { + format!("https://{}/{repo}/commits/{commit_ref}", self.domain(),) + } + } + } +} + +impl FromStr for Platform { + type Err = Error; + + fn from_str(s: &str) -> Result { + match s { + "github" => Ok(Self::GitHub), + "gitlab" => Ok(Self::Gitlab), + "bitbucket" => Ok(Self::Bitbucket), + "sourcehut" => Ok(Self::Sourcehut), + _ => Err(Error::UnknownPlatform(s.to_string())), + } + } +} diff --git a/src/service.rs b/src/service.rs deleted file mode 100644 index f9acd957..00000000 --- a/src/service.rs +++ /dev/null @@ -1,144 +0,0 @@ -use crate::http::{calculate_hoc, delete_repo_and_cache, json_hoc, overview}; - -use actix_web::{ - App, - dev::{ServiceFactory, ServiceRequest}, - web, -}; -use serde::{Deserialize, Serialize}; - -pub(crate) trait Service: Sized + 'static { - fn domain() -> &'static str; - fn url_path() -> &'static str; - fn commit_url(repo: &str, commit_ref: &str) -> String; - fn form_value() -> FormValue; - - fn register_service(app: App) -> App - where - T: ServiceFactory, - { - let url_path = Self::url_path(); - app.service( - web::resource(format!("/{url_path}/{{user}}/{{repo}}")).to(calculate_hoc::), - ) - .service( - web::resource(format!("/{url_path}/{{user}}/{{repo}}/delete")) - .route(web::post().to(delete_repo_and_cache::)), - ) - .service(web::resource(format!("/{url_path}/{{user}}/{{repo}}/json")).to(json_hoc::)) - .service(web::resource(format!("/view/{url_path}/{{user}}/{{repo}}")).to(overview::)) - .service(web::resource(format!("/{url_path}/{{user}}/{{repo}}/view")).to(overview::)) - } -} - -#[derive(Deserialize, Serialize, Clone, Copy, Hash, Eq, PartialEq, Debug)] -pub enum FormValue { - #[serde(rename = "github")] - GitHub, - #[serde(rename = "gitlab")] - Gitlab, - #[serde(rename = "bitbucket")] - Bitbucket, - #[serde(rename = "sourcehut")] - Sourcehut, -} - -impl FormValue { - pub(crate) fn url(&self) -> &str { - match self { - FormValue::GitHub => "github.com", - FormValue::Gitlab => "gitlab.com", - FormValue::Bitbucket => "bitbucket.org", - FormValue::Sourcehut => "git.sr.ht", - } - } - - pub(crate) fn service(&self) -> &str { - match self { - FormValue::GitHub => "github", - FormValue::Gitlab => "gitlab", - FormValue::Bitbucket => "bitbucket", - FormValue::Sourcehut => "sourcehut", - } - } -} - -pub(crate) struct GitHub; - -impl Service for GitHub { - fn domain() -> &'static str { - "github.com" - } - - fn url_path() -> &'static str { - "github" - } - - fn commit_url(repo: &str, commit_ref: &str) -> String { - format!("https://{}/{}/commit/{}", Self::domain(), repo, commit_ref) - } - - fn form_value() -> FormValue { - FormValue::GitHub - } -} - -pub(crate) struct Gitlab; - -impl Service for Gitlab { - fn domain() -> &'static str { - "gitlab.com" - } - - fn url_path() -> &'static str { - "gitlab" - } - - fn commit_url(repo: &str, commit_ref: &str) -> String { - format!("https://{}/{}/commit/{}", Self::domain(), repo, commit_ref) - } - - fn form_value() -> FormValue { - FormValue::Gitlab - } -} - -pub(crate) struct Bitbucket; - -impl Service for Bitbucket { - fn domain() -> &'static str { - "bitbucket.org" - } - - fn url_path() -> &'static str { - "bitbucket" - } - - fn commit_url(repo: &str, commit_ref: &str) -> String { - format!("https://{}/{}/commits/{}", Self::domain(), repo, commit_ref) - } - - fn form_value() -> FormValue { - FormValue::Bitbucket - } -} - -pub(crate) struct Sourcehut; - -impl Service for Sourcehut { - fn domain() -> &'static str { - "git.sr.ht" - } - - fn url_path() -> &'static str { - "sourcehut" - } - - fn commit_url(repo: &str, commit_ref: &str) -> String { - format!("https://{}/{}/commit/{}", Self::domain(), repo, commit_ref) - } - - fn form_value() -> FormValue { - FormValue::Sourcehut - } -} diff --git a/src/telemetry.rs b/src/telemetry.rs index 6ada142c..7be120d3 100644 --- a/src/telemetry.rs +++ b/src/telemetry.rs @@ -1,18 +1,19 @@ use tracing::{Subscriber, subscriber::set_global_default}; -use tracing_bunyan_formatter::{BunyanFormattingLayer, JsonStorageLayer}; use tracing_log::LogTracer; -use tracing_subscriber::{EnvFilter, Registry, layer::SubscriberExt}; +use tracing_subscriber::{EnvFilter, layer::SubscriberExt}; -pub fn get_subscriber(name: &str, env_filter: &str) -> impl Subscriber + Send + Sync { - let env_filter = - EnvFilter::try_from_default_env().unwrap_or_else(|_| EnvFilter::new(env_filter)); +#[must_use] +pub fn get_subscriber(level: &str) -> impl Subscriber + Send + Sync { + let env_filter = EnvFilter::new(std::env::var("RUST_LOG").unwrap_or_else(|_| { + format!( + "{}={level},tower_http=debug,axum::rejection=trace", + env!("CARGO_CRATE_NAME") + ) + })); - let formatting_layer = BunyanFormattingLayer::new(name.to_string(), std::io::stdout); - - Registry::default() + tracing_subscriber::registry() .with(env_filter) - .with(JsonStorageLayer) - .with(formatting_layer) + .with(tracing_subscriber::fmt::layer()) } /// # Panics diff --git a/src/template.rs b/src/template.rs index 05918c42..51f6d28c 100644 --- a/src/template.rs +++ b/src/template.rs @@ -1,4 +1,4 @@ -use crate::service::FormValue; +use crate::platform::Platform; #[derive(Clone, Copy)] pub struct RepoInfo<'a> { @@ -14,7 +14,7 @@ pub struct RepoInfo<'a> { } pub struct RepoGeneratorInfo<'a> { - pub service: FormValue, + pub platform: Platform, pub user: &'a str, pub repo: &'a str, pub branch: &'a str, diff --git a/templates/generate.rs.html b/templates/generate.rs.html index 15c9b85d..91190a08 100644 --- a/templates/generate.rs.html +++ b/templates/generate.rs.html @@ -6,11 +6,11 @@ @:base_html("Hits-of-Code Badges", "Badge Generator", {

-Here is the markdown for the badge for @repo_info.user/@repo_info.repo +Here is the markdown for the badge for @repo_info.user/@repo_info.repo

-[![Hits-of-Code](@base_url/@repo_info.service.service()/@repo_info.user/@repo_info.repo?branch=@repo_info.branch)](@base_url/@repo_info.service.service()/@repo_info.user/@repo_info.repo/view?branch=@repo_info.branch)
+[![Hits-of-Code](@base_url/@repo_info.platform.url_path()/@repo_info.user/@repo_info.repo?branch=@repo_info.branch)](@base_url/@repo_info.platform.url_path()/@repo_info.user/@repo_info.repo/view?branch=@repo_info.branch)
 

@@ -18,6 +18,6 @@

-example badge
+example badge
 
}, version_info, repo_count) diff --git a/tests/badge.rs b/tests/badge.rs index 904cd691..ba5e8d57 100644 --- a/tests/badge.rs +++ b/tests/badge.rs @@ -1,16 +1,24 @@ mod util; -#[actix_rt::test] +use axum::{body::Body, http::Request}; + +#[tokio::test] async fn badge_succeeds() { - let test_app = util::spawn_app().await; + let (_test_app, handle, addr) = util::spawn_app().await; - let client = awc::Client::default(); + let client = hyper_util::client::legacy::Client::builder(hyper_util::rt::TokioExecutor::new()) + .build_http(); let response = client - .get(&format!("{}/github/vbrandl/hoc", test_app.address)) - .send() + .request( + Request::builder() + .uri(format!("http://{addr}/github/vbrandl/hoc")) + .body(Body::empty()) + .unwrap(), + ) .await - .expect("Failed to execute request"); + .unwrap(); assert!(response.status().is_success()); + handle.abort(); } diff --git a/tests/health_check.rs b/tests/health_check.rs index ebcd5983..dcfb6ef9 100644 --- a/tests/health_check.rs +++ b/tests/health_check.rs @@ -1,16 +1,25 @@ +use axum::{body::Body, http::Request}; + mod util; -#[actix_rt::test] +#[tokio::test] async fn health_check_works() { - let test_app = util::spawn_app().await; + // let test_app = util::spawn_app().await; + let (_test_app, handle, addr) = util::spawn_app().await; - let client = awc::Client::default(); + let client = hyper_util::client::legacy::Client::builder(hyper_util::rt::TokioExecutor::new()) + .build_http(); let response = client - .get(&format!("{}/health_check", test_app.address)) - .send() + .request( + Request::builder() + .uri(format!("http://{addr}/health_check")) + .body(Body::empty()) + .unwrap(), + ) .await - .expect("Failed to execute request"); + .unwrap(); assert!(response.status().is_success()); + handle.abort(); } diff --git a/tests/index.rs b/tests/index.rs index 261c2767..30544ad7 100644 --- a/tests/index.rs +++ b/tests/index.rs @@ -1,16 +1,25 @@ +use axum::{body::Body, http::Request}; + mod util; -#[actix_rt::test] +#[tokio::test] async fn index_returns_success() { - let test_app = util::spawn_app().await; + let (_test_app, handle, addr) = util::spawn_app().await; - let client = awc::Client::default(); + let client = hyper_util::client::legacy::Client::builder(hyper_util::rt::TokioExecutor::new()) + .build_http(); let response = client - .get(&format!("{}/", test_app.address)) - .send() + .request( + Request::builder() + .uri(format!("http://{addr}/")) + .body(Body::empty()) + .unwrap(), + ) .await - .expect("Failed to execute request"); + .unwrap(); assert!(response.status().is_success()); + + handle.abort(); } diff --git a/tests/json.rs b/tests/json.rs index 629bd0e6..263e65fc 100644 --- a/tests/json.rs +++ b/tests/json.rs @@ -1,16 +1,28 @@ +use axum::{body::Body, http::Request}; + mod util; -#[actix_rt::test] +#[tokio::test] async fn json_returns_success() { - let test_app = util::spawn_app().await; + let (_test_app, handle, addr) = util::spawn_app().await; - let client = awc::Client::default(); + let client = hyper_util::client::legacy::Client::builder(hyper_util::rt::TokioExecutor::new()) + .build_http(); let response = client - .get(&format!("{}/github/vbrandl/hoc/json", test_app.address)) - .send() + .request( + Request::builder() + .uri(format!("http://{addr}/github/vbrandl/hoc/json")) + .body(Body::empty()) + .unwrap(), + ) .await - .expect("Failed to execute request"); + .unwrap(); assert!(response.status().is_success()); + assert_eq!( + response.headers().get("content-type").unwrap(), + "application/json" + ); + handle.abort(); } diff --git a/tests/resources.rs b/tests/resources.rs index 15540588..ba54bbed 100644 --- a/tests/resources.rs +++ b/tests/resources.rs @@ -1,16 +1,25 @@ +use axum::{body::Body, http::Request}; + mod util; -#[actix_rt::test] +#[tokio::test] async fn favicon() { - let test_app = util::spawn_app().await; + let (_test_app, handle, addr) = util::spawn_app().await; - let client = awc::Client::default(); + let client = hyper_util::client::legacy::Client::builder(hyper_util::rt::TokioExecutor::new()) + .build_http(); let response = client - .get(&format!("{}/favicon.ico", test_app.address)) - .send() + .request( + Request::builder() + .uri(format!("http://{addr}/favicon.ico")) + .body(Body::empty()) + .unwrap(), + ) .await - .expect("Failed to execute request"); + .unwrap(); assert!(response.status().is_success()); + + handle.abort(); } diff --git a/tests/util/mod.rs b/tests/util/mod.rs index fa142ec8..c9260429 100644 --- a/tests/util/mod.rs +++ b/tests/util/mod.rs @@ -1,8 +1,9 @@ -use hoc::{config::Settings, telemetry}; +use hoc::{config::Settings, http, telemetry}; -use std::{net::TcpListener, sync::LazyLock}; +use std::{net::SocketAddr, sync::LazyLock}; use tempfile::{TempDir, tempdir}; +use tokio::task::JoinHandle; static TRACING: LazyLock<()> = LazyLock::new(|| { let filter = if std::env::var("TEST_LOG").is_ok() { @@ -10,43 +11,37 @@ static TRACING: LazyLock<()> = LazyLock::new(|| { } else { "" }; - let subscriber = telemetry::get_subscriber("test", filter); + let subscriber = telemetry::get_subscriber(filter); telemetry::init_subscriber(subscriber); }); pub struct TestApp { - pub address: String, // Those are unused but are hold to be dropped and deleted after the TestApp goes out of scope _repo_dir: TempDir, _cache_dir: TempDir, } -pub async fn spawn_app() -> TestApp { +pub async fn spawn_app() -> (TestApp, JoinHandle<()>, SocketAddr) { LazyLock::force(&TRACING); - let listener = TcpListener::bind("127.0.0.1:0").expect("Failed to bind random port"); - - let port = listener.local_addr().unwrap().port(); - let address = format!("http://127.0.0.1:{port}"); - let repo_dir = tempdir().expect("Cannot create repo_dir"); let cache_dir = tempdir().expect("Cannot create cache_dir"); let mut settings = Settings::load().expect("Failed to read configuration."); + settings.port = 0; settings.repodir = repo_dir.path().to_path_buf(); settings.cachedir = cache_dir.path().to_path_buf(); - let server = hoc::run(listener, settings) - .await - .expect("Failed to bind address"); - - #[allow(clippy::let_underscore_future)] - // don't await so the test server runs in the background - let _ = tokio::spawn(server); - - TestApp { - address, - _repo_dir: repo_dir, - _cache_dir: cache_dir, - } + let listener = settings.listener().await.unwrap(); + let app = http::router(settings).into_make_service_with_connect_info::(); + let addr = listener.local_addr().unwrap(); + + ( + TestApp { + _repo_dir: repo_dir, + _cache_dir: cache_dir, + }, + tokio::spawn(async move { axum::serve(listener, app).await.unwrap() }), + addr, + ) }