Initial commit
Diff
.gitignore | 2 ++
Cargo.lock | 1249 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Cargo.toml | 24 ++++++++++++++++++++++++
LICENSE | 14 ++++++++++++++
README.md | 4 ++++
src/git.rs | 136 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
src/main.rs | 48 ++++++++++++++++++++++++++++++++++++++++++++++++
statics/style.css | 70 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
templates/base.html | 26 ++++++++++++++++++++++++++
templates/index.html | 55 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
src/layers/logger.rs | 134 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
src/layers/mod.rs | 19 +++++++++++++++++++
src/methods/index.rs | 21 +++++++++++++++++++++
src/methods/mod.rs | 2 ++
src/methods/repo.rs | 174 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
templates/repo/about.html | 6 ++++++
templates/repo/base.html | 14 ++++++++++++++
templates/repo/commit.html | 38 ++++++++++++++++++++++++++++++++++++++
templates/repo/diff.html | 6 ++++++
templates/repo/log.html | 6 ++++++
templates/repo/refs.html | 6 ++++++
templates/repo/stats.html | 6 ++++++
templates/repo/summary.html | 6 ++++++
templates/repo/tree.html | 6 ++++++
24 files changed, 2072 insertions(+)
@@ -1,0 +1,2 @@
/target
/.idea
@@ -1,0 +1,1249 @@
version = 3
[[package]]
name = "ansi_term"
version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2"
dependencies = [
"winapi",
]
[[package]]
name = "askama"
version = "0.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fb98f10f371286b177db5eeb9a6e5396609555686a35e1d4f7b9a9c6d8af0139"
dependencies = [
"askama_derive",
"askama_escape",
"askama_shared",
]
[[package]]
name = "askama_derive"
version = "0.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "87bf87e6e8b47264efa9bde63d6225c6276a52e05e91bf37eaa8afd0032d6b71"
dependencies = [
"askama_shared",
"proc-macro2",
"syn",
]
[[package]]
name = "askama_escape"
version = "0.10.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "619743e34b5ba4e9703bba34deac3427c72507c7159f5fd030aea8cac0cfe341"
[[package]]
name = "askama_shared"
version = "0.12.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bf722b94118a07fcbc6640190f247334027685d4e218b794dbfe17c32bf38ed0"
dependencies = [
"askama_escape",
"humansize",
"mime",
"mime_guess",
"nom",
"num-traits",
"percent-encoding",
"proc-macro2",
"quote",
"serde",
"syn",
"toml",
]
[[package]]
name = "async-trait"
version = "0.1.56"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "96cf8829f67d2eab0b2dfa42c5d0ef737e0724e4a82b01b3e292456202b19716"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "atty"
version = "0.2.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
dependencies = [
"hermit-abi",
"libc",
"winapi",
]
[[package]]
name = "autocfg"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
[[package]]
name = "axum"
version = "0.5.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c2cc6e8e8c993cb61a005fab8c1e5093a29199b7253b05a6883999312935c1ff"
dependencies = [
"async-trait",
"axum-core",
"bitflags",
"bytes",
"futures-util",
"http",
"http-body",
"hyper",
"itoa",
"matchit",
"memchr",
"mime",
"percent-encoding",
"pin-project-lite",
"serde",
"serde_json",
"serde_urlencoded",
"sync_wrapper",
"tokio",
"tower",
"tower-http",
"tower-layer",
"tower-service",
]
[[package]]
name = "axum-core"
version = "0.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf4d047478b986f14a13edad31a009e2e05cb241f9805d0d75e4cba4e129ad4d"
dependencies = [
"async-trait",
"bytes",
"futures-util",
"http",
"http-body",
"mime",
]
[[package]]
name = "bitflags"
version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
name = "bytes"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c4872d67bab6358e59559027aa3b9157c53d9358c51423c17554809a8858e0f8"
[[package]]
name = "cc"
version = "1.0.73"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11"
dependencies = [
"jobserver",
]
[[package]]
name = "cfg-if"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "clap"
version = "3.2.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "190814073e85d238f31ff738fcb0bf6910cedeb73376c87cd69291028966fd83"
dependencies = [
"atty",
"bitflags",
"clap_lex",
"indexmap",
"once_cell",
"strsim",
"termcolor",
"textwrap",
]
[[package]]
name = "clap_lex"
version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2850f2f5a82cbf437dd5af4d49848fbdfc27c157c3d010345776f952765261c5"
dependencies = [
"os_str_bytes",
]
[[package]]
name = "fnv"
version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
[[package]]
name = "form_urlencoded"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5fc25a87fa4fd2094bffb06925852034d90a17f0d1e05197d4956d3555752191"
dependencies = [
"matches",
"percent-encoding",
]
[[package]]
name = "futures"
version = "0.3.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f73fe65f54d1e12b726f517d3e2135ca3125a437b6d998caf1962961f7172d9e"
dependencies = [
"futures-channel",
"futures-core",
"futures-executor",
"futures-io",
"futures-sink",
"futures-task",
"futures-util",
]
[[package]]
name = "futures-channel"
version = "0.3.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c3083ce4b914124575708913bca19bfe887522d6e2e6d0952943f5eac4a74010"
dependencies = [
"futures-core",
"futures-sink",
]
[[package]]
name = "futures-core"
version = "0.3.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0c09fd04b7e4073ac7156a9539b57a484a8ea920f79c7c675d05d289ab6110d3"
[[package]]
name = "futures-executor"
version = "0.3.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9420b90cfa29e327d0429f19be13e7ddb68fa1cccb09d65e5706b8c7a749b8a6"
dependencies = [
"futures-core",
"futures-task",
"futures-util",
]
[[package]]
name = "futures-io"
version = "0.3.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fc4045962a5a5e935ee2fdedaa4e08284547402885ab326734432bed5d12966b"
[[package]]
name = "futures-macro"
version = "0.3.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "33c1e13800337f4d4d7a316bf45a567dbcb6ffe087f16424852d97e97a91f512"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "futures-sink"
version = "0.3.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "21163e139fa306126e6eedaf49ecdb4588f939600f0b1e770f4205ee4b7fa868"
[[package]]
name = "futures-task"
version = "0.3.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "57c66a976bf5909d801bbef33416c41372779507e7a6b3a5e25e4749c58f776a"
[[package]]
name = "futures-util"
version = "0.3.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d8b7abd5d659d9b90c8cba917f6ec750a74e2dc23902ef9cd4cc8c8b22e6036a"
dependencies = [
"futures-channel",
"futures-core",
"futures-io",
"futures-macro",
"futures-sink",
"futures-task",
"memchr",
"pin-project-lite",
"pin-utils",
"slab",
]
[[package]]
name = "getrandom"
version = "0.2.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4eb1a864a501629691edf6c15a593b7a51eebaa1e8468e9ddc623de7c9b58ec6"
dependencies = [
"cfg-if",
"libc",
"wasi",
]
[[package]]
name = "git2"
version = "0.14.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d0155506aab710a86160ddb504a480d2964d7ab5b9e62419be69e0032bc5931c"
dependencies = [
"bitflags",
"libc",
"libgit2-sys",
"log",
"openssl-probe",
"openssl-sys",
"url",
]
[[package]]
name = "hashbrown"
version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "db0d4cf898abf0081f964436dc980e96670a0f36863e4b83aaacdb65c9d7ccc3"
[[package]]
name = "hermit-abi"
version = "0.1.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33"
dependencies = [
"libc",
]
[[package]]
name = "http"
version = "0.2.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "75f43d41e26995c17e71ee126451dd3941010b0514a81a9d11f3b341debc2399"
dependencies = [
"bytes",
"fnv",
"itoa",
]
[[package]]
name = "http-body"
version = "0.4.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d5f38f16d184e36f2408a55281cd658ecbd3ca05cce6d6510a176eca393e26d1"
dependencies = [
"bytes",
"http",
"pin-project-lite",
]
[[package]]
name = "http-range-header"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0bfe8eed0a9285ef776bb792479ea3834e8b94e13d615c2f66d03dd50a435a29"
[[package]]
name = "httparse"
version = "1.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "496ce29bb5a52785b44e0f7ca2847ae0bb839c9bd28f69acac9b99d461c0c04c"
[[package]]
name = "httpdate"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421"
[[package]]
name = "humansize"
version = "1.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "02296996cb8796d7c6e3bc2d9211b7802812d36999a51bb754123ead7d37d026"
[[package]]
name = "humantime"
version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4"
[[package]]
name = "hyper"
version = "0.14.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "42dc3c131584288d375f2d07f822b0cb012d8c6fb899a5b9fdb3cb7eb9b6004f"
dependencies = [
"bytes",
"futures-channel",
"futures-core",
"futures-util",
"http",
"http-body",
"httparse",
"httpdate",
"itoa",
"pin-project-lite",
"socket2",
"tokio",
"tower-service",
"tracing",
"want",
]
[[package]]
name = "idna"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "418a0a6fab821475f634efe3ccc45c013f742efe03d853e8d3355d5cb850ecf8"
dependencies = [
"matches",
"unicode-bidi",
"unicode-normalization",
]
[[package]]
name = "indexmap"
version = "1.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "10a35a97730320ffe8e2d410b5d3b69279b98d2c14bdb8b70ea89ecf7888d41e"
dependencies = [
"autocfg",
"hashbrown",
]
[[package]]
name = "itoa"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "112c678d4050afce233f4f2852bb2eb519230b3cf12f33585275537d7e41578d"
[[package]]
name = "jobserver"
version = "0.1.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "af25a77299a7f711a01975c35a6a424eb6862092cc2d6c72c4ed6cbc56dfc1fa"
dependencies = [
"libc",
]
[[package]]
name = "lazy_static"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
[[package]]
name = "libc"
version = "0.2.126"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "349d5a591cd28b49e1d1037471617a32ddcda5731b99419008085f72d5a53836"
[[package]]
name = "libgit2-sys"
version = "0.13.4+1.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d0fa6563431ede25f5cc7f6d803c6afbc1c5d3ad3d4925d12c882bf2b526f5d1"
dependencies = [
"cc",
"libc",
"libssh2-sys",
"libz-sys",
"openssl-sys",
"pkg-config",
]
[[package]]
name = "libssh2-sys"
version = "0.2.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b094a36eb4b8b8c8a7b4b8ae43b2944502be3e59cd87687595cf6b0a71b3f4ca"
dependencies = [
"cc",
"libc",
"libz-sys",
"openssl-sys",
"pkg-config",
"vcpkg",
]
[[package]]
name = "libz-sys"
version = "1.1.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9702761c3935f8cc2f101793272e202c72b99da8f4224a19ddcf1279a6450bbf"
dependencies = [
"cc",
"libc",
"pkg-config",
"vcpkg",
]
[[package]]
name = "lock_api"
version = "0.4.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "327fa5b6a6940e4699ec49a9beae1ea4845c6bab9314e4f84ac68742139d8c53"
dependencies = [
"autocfg",
"scopeguard",
]
[[package]]
name = "log"
version = "0.4.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e"
dependencies = [
"cfg-if",
]
[[package]]
name = "matches"
version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a3e378b66a060d48947b590737b30a1be76706c8dd7b8ba0f2fe3989c68a853f"
[[package]]
name = "matchit"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "73cbba799671b762df5a175adf59ce145165747bb891505c43d09aefbbf38beb"
[[package]]
name = "memchr"
version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
[[package]]
name = "mime"
version = "0.3.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d"
[[package]]
name = "mime_guess"
version = "2.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4192263c238a5f0d0c6bfd21f336a313a4ce1c450542449ca191bb657b4642ef"
dependencies = [
"mime",
"unicase",
]
[[package]]
name = "minimal-lexical"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
[[package]]
name = "mio"
version = "0.8.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "57ee1c23c7c63b0c9250c339ffdc69255f110b298b901b9f6c82547b7b87caaf"
dependencies = [
"libc",
"log",
"wasi",
"windows-sys",
]
[[package]]
name = "nom"
version = "7.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a8903e5a29a317527874d0402f867152a3d21c908bb0b933e416c65e301d4c36"
dependencies = [
"memchr",
"minimal-lexical",
]
[[package]]
name = "num-traits"
version = "0.2.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd"
dependencies = [
"autocfg",
]
[[package]]
name = "num_cpus"
version = "1.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "19e64526ebdee182341572e50e9ad03965aa510cd94427a4549448f285e957a1"
dependencies = [
"hermit-abi",
"libc",
]
[[package]]
name = "num_threads"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2819ce041d2ee131036f4fc9d6ae7ae125a3a40e97ba64d04fe799ad9dabbb44"
dependencies = [
"libc",
]
[[package]]
name = "once_cell"
version = "1.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "18a6dbe30758c9f83eb00cbea4ac95966305f5a7772f3f42ebfc7fc7eddbd8e1"
[[package]]
name = "openssl-probe"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf"
[[package]]
name = "openssl-sys"
version = "0.9.74"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "835363342df5fba8354c5b453325b110ffd54044e588c539cf2f20a8014e4cb1"
dependencies = [
"autocfg",
"cc",
"libc",
"pkg-config",
"vcpkg",
]
[[package]]
name = "os_str_bytes"
version = "6.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "21326818e99cfe6ce1e524c2a805c189a99b5ae555a35d19f9a284b427d86afa"
[[package]]
name = "owning_ref"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6ff55baddef9e4ad00f88b6c743a2a8062d4c6ade126c2a528644b8e444d52ce"
dependencies = [
"stable_deref_trait",
]
[[package]]
name = "parking_lot"
version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f"
dependencies = [
"lock_api",
"parking_lot_core",
]
[[package]]
name = "parking_lot_core"
version = "0.9.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09a279cbf25cb0757810394fbc1e359949b59e348145c643a939a525692e6929"
dependencies = [
"cfg-if",
"libc",
"redox_syscall",
"smallvec",
"windows-sys",
]
[[package]]
name = "path-clean"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ecba01bf2678719532c5e3059e0b5f0811273d94b397088b82e3bd0a78c78fdd"
[[package]]
name = "percent-encoding"
version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e"
[[package]]
name = "pin-project"
version = "1.0.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "78203e83c48cffbe01e4a2d35d566ca4de445d79a85372fc64e378bfc812a260"
dependencies = [
"pin-project-internal",
]
[[package]]
name = "pin-project-internal"
version = "1.0.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "710faf75e1b33345361201d36d04e98ac1ed8909151a017ed384700836104c74"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "pin-project-lite"
version = "0.2.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116"
[[package]]
name = "pin-utils"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
[[package]]
name = "pkg-config"
version = "0.3.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1df8c4ec4b0627e53bdf214615ad287367e482558cf84b109250b37464dc03ae"
[[package]]
name = "proc-macro2"
version = "1.0.40"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dd96a1e8ed2596c337f8eae5f24924ec83f5ad5ab21ea8e455d3566c69fbcaf7"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3bcdf212e9776fbcb2d23ab029360416bb1706b1aea2d1a5ba002727cbcab804"
dependencies = [
"proc-macro2",
]
[[package]]
name = "redox_syscall"
version = "0.2.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "62f25bc4c7e55e0b0b7a1d43fb893f4fa1361d0abe38b9ce4f323c2adfe6ef42"
dependencies = [
"bitflags",
]
[[package]]
name = "rgit"
version = "0.1.0"
dependencies = [
"askama",
"axum",
"clap",
"futures",
"git2",
"humantime",
"owning_ref",
"path-clean",
"time",
"tokio",
"tower",
"tower-layer",
"tower-service",
"tracing",
"tracing-subscriber",
"uuid",
]
[[package]]
name = "ryu"
version = "1.0.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f3f6f92acf49d1b98f7a81226834412ada05458b7364277387724a237f062695"
[[package]]
name = "scopeguard"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
[[package]]
name = "serde"
version = "1.0.138"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1578c6245786b9d168c5447eeacfb96856573ca56c9d68fdcf394be134882a47"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.138"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "023e9b1467aef8a10fb88f25611870ada9800ef7e22afce356bb0d2387b6f27c"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "serde_json"
version = "1.0.82"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "82c2c1fdcd807d1098552c5b9a36e425e42e9fbd7c6a37a8425f390f781f7fa7"
dependencies = [
"itoa",
"ryu",
"serde",
]
[[package]]
name = "serde_urlencoded"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd"
dependencies = [
"form_urlencoded",
"itoa",
"ryu",
"serde",
]
[[package]]
name = "sharded-slab"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "900fba806f70c630b0a382d0d825e17a0f19fcd059a2ade1ff237bcddf446b31"
dependencies = [
"lazy_static",
]
[[package]]
name = "signal-hook-registry"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e51e73328dc4ac0c7ccbda3a494dfa03df1de2f46018127f60c693f2648455b0"
dependencies = [
"libc",
]
[[package]]
name = "slab"
version = "0.4.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eb703cfe953bccee95685111adeedb76fabe4e97549a58d16f03ea7b9367bb32"
[[package]]
name = "smallvec"
version = "1.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2fd0db749597d91ff862fd1d55ea87f7855a744a8425a64695b6fca237d1dad1"
[[package]]
name = "socket2"
version = "0.4.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "66d72b759436ae32898a2af0a14218dbf55efde3feeb170eb623637db85ee1e0"
dependencies = [
"libc",
"winapi",
]
[[package]]
name = "stable_deref_trait"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3"
[[package]]
name = "strsim"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
[[package]]
name = "syn"
version = "1.0.98"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c50aef8a904de4c23c788f104b7dddc7d6f79c647c7c8ce4cc8f73eb0ca773dd"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "sync_wrapper"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "20518fe4a4c9acf048008599e464deb21beeae3d3578418951a189c235a7a9a8"
[[package]]
name = "termcolor"
version = "1.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755"
dependencies = [
"winapi-util",
]
[[package]]
name = "textwrap"
version = "0.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b1141d4d61095b28419e22cb0bbf02755f5e54e0526f97f1e3d1d160e60885fb"
[[package]]
name = "thread_local"
version = "1.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5516c27b78311c50bf42c071425c560ac799b11c30b31f87e3081965fe5e0180"
dependencies = [
"once_cell",
]
[[package]]
name = "time"
version = "0.3.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72c91f41dcb2f096c05f0873d667dceec1087ce5bcf984ec8ffb19acddbb3217"
dependencies = [
"libc",
"num_threads",
]
[[package]]
name = "tinyvec"
version = "1.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50"
dependencies = [
"tinyvec_macros",
]
[[package]]
name = "tinyvec_macros"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c"
[[package]]
name = "tokio"
version = "1.19.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c51a52ed6686dd62c320f9b89299e9dfb46f730c7a48e635c19f21d116cb1439"
dependencies = [
"bytes",
"libc",
"memchr",
"mio",
"num_cpus",
"once_cell",
"parking_lot",
"pin-project-lite",
"signal-hook-registry",
"socket2",
"tokio-macros",
"winapi",
]
[[package]]
name = "tokio-macros"
version = "1.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9724f9a975fb987ef7a3cd9be0350edcbe130698af5b8f7a631e23d42d052484"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "toml"
version = "0.5.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8d82e1a7758622a465f8cee077614c73484dac5b836c02ff6a40d5d1010324d7"
dependencies = [
"serde",
]
[[package]]
name = "tower"
version = "0.4.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c"
dependencies = [
"futures-core",
"futures-util",
"pin-project",
"pin-project-lite",
"tokio",
"tower-layer",
"tower-service",
"tracing",
]
[[package]]
name = "tower-http"
version = "0.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3c530c8675c1dbf98facee631536fa116b5fb6382d7dd6dc1b118d970eafe3ba"
dependencies = [
"bitflags",
"bytes",
"futures-core",
"futures-util",
"http",
"http-body",
"http-range-header",
"pin-project-lite",
"tower",
"tower-layer",
"tower-service",
]
[[package]]
name = "tower-layer"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "343bc9466d3fe6b0f960ef45960509f84480bf4fd96f92901afe7ff3df9d3a62"
[[package]]
name = "tower-service"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52"
[[package]]
name = "tracing"
version = "0.1.35"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a400e31aa60b9d44a52a8ee0343b5b18566b03a8321e0d321f695cf56e940160"
dependencies = [
"cfg-if",
"log",
"pin-project-lite",
"tracing-attributes",
"tracing-core",
]
[[package]]
name = "tracing-attributes"
version = "0.1.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "11c75893af559bc8e10716548bdef5cb2b983f8e637db9d0e15126b61b484ee2"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "tracing-core"
version = "0.1.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7b7358be39f2f274f322d2aaed611acc57f382e8eb1e5b48cb9ae30933495ce7"
dependencies = [
"once_cell",
"valuable",
]
[[package]]
name = "tracing-log"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "78ddad33d2d10b1ed7eb9d1f518a5674713876e97e5bb9b7345a7984fbb4f922"
dependencies = [
"lazy_static",
"log",
"tracing-core",
]
[[package]]
name = "tracing-subscriber"
version = "0.3.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3a713421342a5a666b7577783721d3117f1b69a393df803ee17bb73b1e122a59"
dependencies = [
"ansi_term",
"sharded-slab",
"smallvec",
"thread_local",
"tracing-core",
"tracing-log",
]
[[package]]
name = "try-lock"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642"
[[package]]
name = "unicase"
version = "2.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "50f37be617794602aabbeee0be4f259dc1778fabe05e2d67ee8f79326d5cb4f6"
dependencies = [
"version_check",
]
[[package]]
name = "unicode-bidi"
version = "0.3.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "099b7128301d285f79ddd55b9a83d5e6b9e97c92e0ea0daebee7263e932de992"
[[package]]
name = "unicode-ident"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5bd2fe26506023ed7b5e1e315add59d6f584c621d037f9368fea9cfb988f368c"
[[package]]
name = "unicode-normalization"
version = "0.1.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "854cbdc4f7bc6ae19c820d44abdc3277ac3e1b2b93db20a636825d9322fb60e6"
dependencies = [
"tinyvec",
]
[[package]]
name = "url"
version = "2.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a507c383b2d33b5fc35d1861e77e6b383d158b2da5e14fe51b83dfedf6fd578c"
dependencies = [
"form_urlencoded",
"idna",
"matches",
"percent-encoding",
]
[[package]]
name = "uuid"
version = "1.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dd6469f4314d5f1ffec476e05f17cc9a78bc7a27a6a857842170bdf8d6f98d2f"
dependencies = [
"getrandom",
]
[[package]]
name = "valuable"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d"
[[package]]
name = "vcpkg"
version = "0.2.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
[[package]]
name = "version_check"
version = "0.9.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
[[package]]
name = "want"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0"
dependencies = [
"log",
"try-lock",
]
[[package]]
name = "wasi"
version = "0.11.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
[[package]]
name = "winapi"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
dependencies = [
"winapi-i686-pc-windows-gnu",
"winapi-x86_64-pc-windows-gnu",
]
[[package]]
name = "winapi-i686-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
[[package]]
name = "winapi-util"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178"
dependencies = [
"winapi",
]
[[package]]
name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "windows-sys"
version = "0.36.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ea04155a16a59f9eab786fe12a4a450e75cdb175f9e0d80da1e17db09f55b8d2"
dependencies = [
"windows_aarch64_msvc",
"windows_i686_gnu",
"windows_i686_msvc",
"windows_x86_64_gnu",
"windows_x86_64_msvc",
]
[[package]]
name = "windows_aarch64_msvc"
version = "0.36.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9bb8c3fd39ade2d67e9874ac4f3db21f0d710bee00fe7cab16949ec184eeaa47"
[[package]]
name = "windows_i686_gnu"
version = "0.36.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "180e6ccf01daf4c426b846dfc66db1fc518f074baa793aa7d9b9aaeffad6a3b6"
[[package]]
name = "windows_i686_msvc"
version = "0.36.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024"
[[package]]
name = "windows_x86_64_gnu"
version = "0.36.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4dcd171b8776c41b97521e5da127a2d86ad280114807d0b2ab1e462bc764d9e1"
[[package]]
name = "windows_x86_64_msvc"
version = "0.36.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680"
@@ -1,0 +1,24 @@
[package]
name = "rgit"
version = "0.1.0"
edition = "2021"
[dependencies]
askama = "0.11"
axum = "0.5"
clap = { version = "3.2", features = ["cargo"] }
futures = "0.3"
git2 = "0.14"
humantime = "2.1"
path-clean = "0.1"
owning_ref = "0.4"
time = "0.3"
tokio = { version = "1.19", features = ["full"] }
tower = "0.4"
tower-service = "0.3"
tower-layer = "0.3"
tracing = "0.1"
tracing-subscriber = "0.3"
uuid = { version = "1.1", features = ["v4"] }
@@ -1,0 +1,14 @@
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
Version 2, December 2004
Copyright (C) 2004 Sam Hocevar <sam@hocevar.net>
Everyone is permitted to copy and distribute verbatim or modified
copies of this license document, and changing it is allowed as long
as the name is changed.
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. You just DO WHAT THE FUCK YOU WANT TO.
@@ -1,0 +1,4 @@
# rgit
A gitweb/cgit-like interface for the modern age. Written in Rust using Axum, git2 and
Askama. No more CGI, how exciting.
@@ -1,0 +1,136 @@
use std::{borrow::Cow, collections::BTreeMap, fmt::Display, path::Path, time::Duration};
use git2::{Repository, Signature};
use owning_ref::OwningHandle;
use time::OffsetDateTime;
pub type RepositoryMetadataList = BTreeMap<Option<String>, Vec<RepositoryMetadata>>;
#[derive(Debug)]
pub struct RepositoryMetadata {
pub name: String,
pub description: Option<Cow<'static, str>>,
pub owner: Option<String>,
pub last_modified: Duration,
}
pub struct CommitUser<'a>(Signature<'a>);
impl CommitUser<'_> {
pub fn name(&self) -> &str {
self.0.name().unwrap()
}
pub fn email(&self) -> &str {
self.0.email().unwrap()
}
pub fn time(&self) -> String {
OffsetDateTime::from_unix_timestamp(self.0.when().seconds())
.unwrap()
.to_string()
}
}
pub struct Commit(OwningHandle<Box<Repository>, Box<git2::Commit<'static>>>);
impl Commit {
pub fn author(&self) -> CommitUser<'_> {
CommitUser(self.0.author())
}
pub fn committer(&self) -> CommitUser<'_> {
CommitUser(self.0.committer())
}
pub fn oid(&self) -> impl Display {
self.0.id()
}
pub fn tree(&self) -> impl Display {
self.0.tree_id()
}
pub fn parents(&self) -> impl Iterator<Item = impl Display + '_> {
self.0.parent_ids()
}
pub fn summary(&self) -> &str {
self.0.summary().unwrap()
}
pub fn body(&self) -> &str {
self.0.message().unwrap()
}
}
pub fn get_latest_commit(path: &Path) -> Commit {
let repo = Repository::open_bare(path).unwrap();
let commit = OwningHandle::new_with_fn(Box::new(repo), |v| {
let head = unsafe { (*v).head().unwrap() };
Box::new(head.peel_to_commit().unwrap())
});
Commit(commit)
}
pub fn fetch_repository_metadata() -> RepositoryMetadataList {
let start = Path::new("../test-git").canonicalize().unwrap();
let mut repos: RepositoryMetadataList = RepositoryMetadataList::new();
fetch_repository_metadata_impl(&start, &start, &mut repos);
repos
}
fn fetch_repository_metadata_impl(
start: &Path,
current: &Path,
repos: &mut RepositoryMetadataList,
) {
let dirs = std::fs::read_dir(current)
.unwrap()
.map(|v| v.unwrap().path())
.filter(|path| path.is_dir());
for dir in dirs {
let repository = match Repository::open_bare(&dir) {
Ok(v) => v,
Err(_e) => {
fetch_repository_metadata_impl(start, &dir, repos);
continue;
}
};
let repo_path = Some(
current
.strip_prefix(start)
.unwrap()
.to_string_lossy()
.into_owned(),
)
.filter(|v| !v.is_empty());
let repos = repos.entry(repo_path).or_default();
let description = std::fs::read_to_string(dir.join("description"))
.map(Cow::Owned)
.ok();
let last_modified = std::fs::metadata(&dir).unwrap().modified().unwrap();
let owner = repository.config().unwrap().get_string("gitweb.owner").ok();
repos.push(RepositoryMetadata {
name: dir
.components()
.last()
.unwrap()
.as_os_str()
.to_string_lossy()
.into_owned(),
description,
owner,
last_modified: (OffsetDateTime::now_utc() - OffsetDateTime::from(last_modified))
.unsigned_abs(),
});
}
}
@@ -1,0 +1,48 @@
#![deny(clippy::pedantic)]
use axum::{
body::Body, handler::Handler, http::HeaderValue, response::Response, routing::get, Router,
};
use tower_layer::layer_fn;
use crate::{
git::{fetch_repository_metadata, get_latest_commit},
layers::logger::LoggingMiddleware,
};
mod git;
mod layers;
mod methods;
const CRATE_VERSION: &str = clap::crate_version!();
#[tokio::main]
async fn main() {
let subscriber = tracing_subscriber::fmt();
#[cfg(debug_assertions)]
let subscriber = subscriber.pretty();
subscriber.init();
let app = Router::new()
.route("/", get(methods::index::handle))
.route(
"/style.css",
get(static_css(include_bytes!("../statics/style.css"))),
)
.fallback(methods::repo::service.into_service())
.layer(layer_fn(LoggingMiddleware));
axum::Server::bind(&"127.0.0.1:3333".parse().unwrap())
.serve(app.into_make_service_with_connect_info::<std::net::SocketAddr>())
.await
.unwrap();
}
fn static_css(content: &'static [u8]) -> impl Handler<()> {
move || async move {
let mut resp = Response::new(Body::from(content));
resp.headers_mut()
.insert("Content-Type", HeaderValue::from_static("text/css"));
resp
}
}
@@ -1,0 +1,70 @@
body {
font-family: sans-serif;
font-size: 10pt;
}
main {
padding: 2rem;
margin: 0;
border-bottom: solid 3px #ccc;
}
header {
border-bottom: solid 1px #ccc;
}
footer {
margin-top: 0.5em;
text-align: center;
font-size: 80%;
color: #ccc;
}
a {
text-decoration: none;
color: blue;
}
a:hover {
text-decoration: underline;
}
table { border-collapse: collapse; }
table.repositories { width: 100%; }
table.repositories a { color: black; }
table.repositories a:hover { color: #00f; }
table th { text-align: left; }
.repo-section { font-style: italic; color: #888; }
table.repositories tbody tr:nth-child(odd) {
background: #f7f7f7;
}
table tbody tr.has-parent td:first-of-type {
padding-left: 1rem;
}
table pre { margin: 0; }
table.commit-info td, table.commit-info th { padding: 0.1em 1em 0.1em 0.1em; }
nav {
margin-top: 2rem;
border-bottom: solid 3px #ccc;
}
nav a {
padding: 2px 0.75em;
color: #777;
font-size: 110%;
text-decoration: none;
}
nav a:hover {
text-decoration: underline;
}
nav a.active {
color: #000;
background-color: #ccc;
}
@@ -1,0 +1,26 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>{% block title %}rgit{% endblock %}</title>
{% block head %}{% endblock %}
<link rel="stylesheet" type="text/css" href="/style.css" />
</head>
<body>
<header>
<h1>{% block header %}Git repository browser{% endblock %}</h1>
</header>
{% block nav %}{% endblock %}
<main>
{% block content %}{% endblock %}
</main>
<footer>
generated by <a href="https://github.com/w4/rgit" target="_blank">rgit</a> v{{ crate::CRATE_VERSION }} at {{ time::OffsetDateTime::now_utc().to_string() }}
</footer>
</body>
</html>
@@ -1,0 +1,55 @@
{% extends "base.html" %}
{% block title %}Index{% endblock %}
{% block content %}
<table class="repositories">
<thead>
<tr>
<th>Name</th>
<th>Description</th>
<th>Owner</th>
<th>Idle</th>
</tr>
</thead>
<tbody>
{% for (path, repositories) in repositories %}
{% if let Some(path) = path %}
<tr><td class="repo-section" colspan="4">{{ path }}</td></tr>
{% endif %}
{%for repository in repositories %}
<tr class="{% if path.is_some() %}has-parent{% endif %}">
<td>
<a href="/{% if let Some(path) = path %}{{ path }}/{% endif %}{{ repository.name }}">
{{ repository.name }}
</a>
</td>
<td>
<a href="/{% if let Some(path) = path %}{{ path }}/{% endif %}{{ repository.name }}">
{% if let Some(description) = repository.description %}
{{ description }}
{% else %}
Unnamed repository; edit this file 'description' to name the repository.
{% endif %}
</a>
</td>
<td>
<a href="/{% if let Some(path) = path %}{{ path }}/{% endif %}{{ repository.name }}">
{% if let Some(owner) = repository.owner %}
{{ owner }}
{% endif %}
</a>
</td>
<td>
<a href="/{% if let Some(path) = path %}{{ path }}/{% endif %}{{ repository.name }}">
{{ humantime::format_duration(repository.last_modified.clone()) }}
</a>
</td>
</tr>
{% endfor %}
{% endfor %}
</tbody>
</table>
{% endblock %}
@@ -1,0 +1,134 @@
use std::{
fmt::Debug,
net::SocketAddr,
task::{Context, Poll},
time::Instant,
};
use axum::{
extract,
http::{HeaderValue, Method, Request, Response},
};
use futures::future::{Future, FutureExt, Join, Map, Ready};
use tower_service::Service;
use tracing::{error, info, instrument::Instrumented, Instrument, Span};
use uuid::Uuid;
use super::UnwrapInfallible;
pub trait GenericError: std::error::Error + Debug + Send + Sync {}
#[derive(Clone)]
pub struct LoggingMiddleware<S>(pub S);
impl<S, ReqBody, ResBody> Service<Request<ReqBody>> for LoggingMiddleware<S>
where
S: Service<Request<ReqBody>, Response = Response<ResBody>, Error = std::convert::Infallible>
+ Clone
+ Send
+ 'static,
S::Future: Send + 'static,
S::Response: Default + Debug,
ReqBody: Send + Debug + 'static,
ResBody: Default + Send + 'static,
{
type Response = S::Response;
type Error = S::Error;
type Future = Map<
Join<Instrumented<S::Future>, Ready<PendingLogMessage>>,
fn((<S::Future as Future>::Output, PendingLogMessage)) -> <S::Future as Future>::Output,
>;
fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
self.0.poll_ready(cx)
}
fn call(&mut self, req: Request<ReqBody>) -> Self::Future {
let request_id = Uuid::new_v4();
let span = tracing::info_span!("web", "request_id" = request_id.to_string().as_str());
let log_message = PendingLogMessage {
span: span.clone(),
request_id,
ip: req
.extensions()
.get::<extract::ConnectInfo<std::net::SocketAddr>>()
.map_or_else(|| "0.0.0.0:0".parse().unwrap(), |v| v.0),
method: req.method().clone(),
uri: req.uri().path().to_string(),
start: Instant::now(),
user_agent: req.headers().get(axum::http::header::USER_AGENT).cloned(),
};
futures::future::join(
self.0.call(req).instrument(span),
futures::future::ready(log_message),
)
.map(|(response, pending_log_message)| {
let mut response = response.unwrap_infallible();
pending_log_message.log(&response);
response.headers_mut().insert(
"X-Request-ID",
HeaderValue::try_from(pending_log_message.request_id.to_string()).unwrap(),
);
Ok(response)
})
}
}
pub struct PendingLogMessage {
span: Span,
request_id: Uuid,
ip: SocketAddr,
method: Method,
uri: String,
start: Instant,
user_agent: Option<HeaderValue>,
}
impl PendingLogMessage {
pub fn log<ResBody>(&self, response: &Response<ResBody>) {
let _enter = self.span.enter();
if response.status().is_server_error() {
error!(
"{ip} - \"{method} {uri}\" {status} {duration:?} \"{user_agent}\" \"{error:?}\"",
ip = self.ip,
method = self.method,
uri = self.uri,
status = response.status().as_u16(),
duration = self.start.elapsed(),
user_agent = self
.user_agent
.as_ref()
.and_then(|v| v.to_str().ok())
.unwrap_or("unknown"),
error = match response.extensions().get::<Box<dyn GenericError>>() {
Some(e) => Err(e),
None => Ok(()),
}
);
} else {
info!(
"{ip} - \"{method} {uri}\" {status} {duration:?} \"{user_agent}\" \"{error:?}\"",
ip = self.ip,
method = self.method,
uri = self.uri,
status = response.status().as_u16(),
duration = self.start.elapsed(),
user_agent = self
.user_agent
.as_ref()
.and_then(|v| v.to_str().ok())
.unwrap_or("unknown"),
error = match response.extensions().get::<Box<dyn GenericError>>() {
Some(e) => Err(e),
None => Ok(()),
}
);
}
}
}
@@ -1,0 +1,19 @@
use std::convert::Infallible;
pub mod logger;
pub trait UnwrapInfallible<T> {
fn unwrap_infallible(self) -> T;
}
impl<T> UnwrapInfallible<T> for Result<T, Infallible> {
fn unwrap_infallible(self) -> T {
self.unwrap()
}
}
impl<T> UnwrapInfallible<T> for Result<T, &Infallible> {
fn unwrap_infallible(self) -> T {
self.unwrap()
}
}
@@ -1,0 +1,21 @@
use askama::Template;
use axum::response::Html;
use crate::{fetch_repository_metadata, git::RepositoryMetadataList};
#[allow(clippy::unused_async)]
pub async fn handle() -> Html<String> {
#[derive(Template)]
#[template(path = "index.html")]
pub struct View {
pub repositories: RepositoryMetadataList,
}
Html(
View {
repositories: fetch_repository_metadata(),
}
.render()
.unwrap(),
)
}
@@ -1,0 +1,2 @@
pub mod index;
pub mod repo;
@@ -1,0 +1,174 @@
use std::{
ops::Deref,
path::{Path, PathBuf},
};
use askama::Template;
use axum::{
handler::Handler,
http::Request,
response::{Html, IntoResponse, Response},
Extension,
};
use path_clean::PathClean;
use tower::{util::BoxCloneService, Service};
use crate::{get_latest_commit, git::Commit, layers::UnwrapInfallible};
#[derive(Clone)]
pub struct Repository(pub PathBuf);
impl Deref for Repository {
type Target = Path;
fn deref(&self) -> &Self::Target {
&self.0
}
}
#[derive(Clone)]
pub struct RepositoryPath(pub PathBuf);
impl Deref for RepositoryPath {
type Target = Path;
fn deref(&self) -> &Self::Target {
&self.0
}
}
pub async fn service<ReqBody: Send + 'static>(mut request: Request<ReqBody>) -> Response {
let mut uri_parts: Vec<&str> = request
.uri()
.path()
.trim_start_matches('/')
.trim_end_matches('/')
.split('/')
.collect();
let mut service = match uri_parts.pop() {
Some("about") => BoxCloneService::new(handle_about.into_service()),
Some("refs") => BoxCloneService::new(handle_refs.into_service()),
Some("log") => BoxCloneService::new(handle_log.into_service()),
Some("tree") => BoxCloneService::new(handle_tree.into_service()),
Some("commit") => BoxCloneService::new(handle_commit.into_service()),
Some("diff") => BoxCloneService::new(handle_diff.into_service()),
Some("stats") => BoxCloneService::new(handle_stats.into_service()),
Some(v) => {
uri_parts.push(v);
BoxCloneService::new(handle_summary.into_service())
}
None => panic!("not found"),
};
let uri = uri_parts.into_iter().collect::<PathBuf>().clean();
let path = Path::new("../test-git").canonicalize().unwrap().join(&uri);
request.extensions_mut().insert(Repository(uri));
request.extensions_mut().insert(RepositoryPath(path));
service
.call(request)
.await
.unwrap_infallible()
.into_response()
}
#[allow(clippy::unused_async)]
pub async fn handle_summary(Extension(repo): Extension<Repository>) -> Html<String> {
#[derive(Template)]
#[template(path = "repo/summary.html")]
pub struct View {
repo: Repository,
}
Html(View { repo }.render().unwrap())
}
#[allow(clippy::unused_async)]
pub async fn handle_log(Extension(repo): Extension<Repository>) -> Html<String> {
#[derive(Template)]
#[template(path = "repo/log.html")]
pub struct View {
repo: Repository,
}
Html(View { repo }.render().unwrap())
}
#[allow(clippy::unused_async)]
pub async fn handle_refs(Extension(repo): Extension<Repository>) -> Html<String> {
#[derive(Template)]
#[template(path = "repo/refs.html")]
pub struct View {
repo: Repository,
}
Html(View { repo }.render().unwrap())
}
#[allow(clippy::unused_async)]
pub async fn handle_about(Extension(repo): Extension<Repository>) -> Html<String> {
#[derive(Template)]
#[template(path = "repo/about.html")]
pub struct View {
repo: Repository,
}
Html(View { repo }.render().unwrap())
}
#[allow(clippy::unused_async)]
pub async fn handle_commit(
Extension(repo): Extension<Repository>,
Extension(RepositoryPath(repository_path)): Extension<RepositoryPath>,
) -> Html<String> {
#[derive(Template)]
#[template(path = "repo/commit.html")]
pub struct View {
pub repo: Repository,
pub commit: Commit,
}
Html(
View {
repo,
commit: get_latest_commit(&repository_path),
}
.render()
.unwrap(),
)
}
#[allow(clippy::unused_async)]
pub async fn handle_tree(Extension(repo): Extension<Repository>) -> Html<String> {
#[derive(Template)]
#[template(path = "repo/tree.html")]
pub struct View {
pub repo: Repository,
}
Html(View { repo }.render().unwrap())
}
#[allow(clippy::unused_async)]
pub async fn handle_diff(Extension(repo): Extension<Repository>) -> Html<String> {
#[derive(Template)]
#[template(path = "repo/diff.html")]
pub struct View {
pub repo: Repository,
}
Html(View { repo }.render().unwrap())
}
#[allow(clippy::unused_async)]
pub async fn handle_stats(Extension(repo): Extension<Repository>) -> Html<String> {
#[derive(Template)]
#[template(path = "repo/stats.html")]
pub struct View {
pub repo: Repository,
}
Html(View { repo }.render().unwrap())
}
@@ -1,0 +1,6 @@
{% extends "repo/base.html" %}
{% block about_nav_class %}active{% endblock %}
{% block content %}
{% endblock %}
@@ -1,0 +1,14 @@
{% extends "../base.html" %}
{% block nav %}
<nav>
<a href="/{{ repo.display() }}/about" class="{% block about_nav_class %}{% endblock %}">about</a>
<a href="/{{ repo.display() }}" class="{% block summary_nav_class %}{% endblock %}">summary</a>
<a href="/{{ repo.display() }}/refs" class="{% block refs_nav_class %}{% endblock %}">refs</a>
<a href="/{{ repo.display() }}/log" class="{% block log_nav_class %}{% endblock %}">log</a>
<a href="/{{ repo.display() }}/tree" class="{% block tree_nav_class %}{% endblock %}">tree</a>
<a href="/{{ repo.display() }}/commit" class="{% block commit_nav_class %}{% endblock %}">commit</a>
<a href="/{{ repo.display() }}/diff" class="{% block diff_nav_class %}{% endblock %}">diff</a>
<a href="/{{ repo.display() }}/stats" class="{% block stats_nav_class %}{% endblock %}">stats</a>
</nav>
{% endblock %}
@@ -1,0 +1,38 @@
{% extends "repo/base.html" %}
{% block commit_nav_class %}active{% endblock %}
{% block content %}
<table class="commit-info">
<tbody>
<tr>
<th>author</th>
<td>{{ commit.author().name() }} <{{ commit.author().email() }}></td>
<td>{{ commit.author().time() }}</td>
</tr>
<tr>
<th>committer</th>
<td>{{ commit.committer().name() }} <{{ commit.committer().email() }}></td>
<td>{{ commit.committer().time() }}</td>
</tr>
<tr>
<th>commit</th>
<td colspan="2"><pre>{{ commit.oid() }}</pre></td>
</tr>
<tr>
<th>tree</th>
<td colspan="2"><pre>{{ commit.tree() }}</pre></td>
</tr>
{% for parent in commit.parents() %}
<tr>
<th>parent</th>
<td colspan="2"><pre>{{ parent }}</pre></td>
</tr>
{% endfor %}
</tbody>
</table>
<h2>{{ commit.summary() }}</h2>
<pre>{{ commit.body() }}</pre>
{% endblock %}
@@ -1,0 +1,6 @@
{% extends "repo/base.html" %}
{% block diff_nav_class %}active{% endblock %}
{% block content %}
{% endblock %}
@@ -1,0 +1,6 @@
{% extends "repo/base.html" %}
{% block log_nav_class %}active{% endblock %}
{% block content %}
{% endblock %}
@@ -1,0 +1,6 @@
{% extends "repo/base.html" %}
{% block refs_nav_class %}active{% endblock %}
{% block content %}
{% endblock %}
@@ -1,0 +1,6 @@
{% extends "repo/base.html" %}
{% block stats_nav_class %}active{% endblock %}
{% block content %}
{% endblock %}
@@ -1,0 +1,6 @@
{% extends "repo/base.html" %}
{% block summary_nav_class %}active{% endblock %}
{% block content %}
{% endblock %}
@@ -1,0 +1,6 @@
{% extends "repo/base.html" %}
{% block tree_nav_class %}active{% endblock %}
{% block content %}
{% endblock %}