commit: | 1951074e8fdd70eb72b3fa3b514a74e1eef3da19 |
author: | Trevor Bentley |
committer: | Trevor Bentley |
date: | Tue Jan 10 01:13:35 2023 +0100 |
parents: | 9b64c36b0615dc2ab2382a59c37777e56c7ffe8f |
diff --git a/Cargo.lock b/Cargo.lock line changes: +1094/-18 index d1f627c..e30c466 --- a/Cargo.lock +++ b/Cargo.lock
@@ -3,6 +3,24 @@ version = 3 [[package]] +name = "aho-corasick" +version = "0.7.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc936419f96fa211c1b9166887b38e5e40b19958e5b895be7c1f93adec7071ac" +dependencies = [ + "memchr", +] + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + +[[package]] name = "autocfg" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -15,6 +33,31 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] +name = "block-buffer" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69cce20737498f97b993470a6e536b8523f0af7892a4f928cceb1ac5e52ebe7e" +dependencies = [ + "generic-array", +] + +[[package]] +name = "bstr" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b45ea9b00a7b3f2988e9a65ad3917e62123c38dba709b666506207be96d1790b" +dependencies = [ + "memchr", + "serde", +] + +[[package]] +name = "bumpalo" +version = "3.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "572f695136211188308f16ad2ca5c851a712c464060ae6974944458eb83880ba" + +[[package]] name = "cc" version = "1.0.78" source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -30,6 +73,202 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] +name = "chrono" +version = "0.4.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16b0a3d9ed01224b22057780a37bb8c5dbfe1be8ba48678e7bf57ec4b385411f" +dependencies = [ + "iana-time-zone", + "js-sys", + "num-integer", + "num-traits", + "time", + "wasm-bindgen", + "winapi", +] + +[[package]] +name = "chrono-tz" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29c39203181991a7dd4343b8005bd804e7a9a37afb8ac070e43771e8c820bbde" +dependencies = [ + "chrono", + "chrono-tz-build", + "phf", +] + +[[package]] +name = "chrono-tz-build" +version = "0.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f509c3a87b33437b05e2458750a0700e5bdd6956176773e6c7d6dd15a283a0c" +dependencies = [ + "parse-zoneinfo", + "phf", + "phf_codegen", +] + +[[package]] +name = "clap" +version = "4.0.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7db700bc935f9e43e88d00b0850dae18a63773cfbec6d8e070fccf7fef89a39" +dependencies = [ + "bitflags", + "clap_derive", + "clap_lex", + "is-terminal", + "once_cell", + "strsim", + "termcolor", +] + +[[package]] +name = "clap_derive" +version = "4.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0177313f9f02afc995627906bbd8967e2be069f5261954222dac78290c2b9014" +dependencies = [ + "heck", + "proc-macro-error", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "clap_lex" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d4198f73e42b4936b35b5bb248d81d2b595ecb170da0bac7655c54eedfa8da8" +dependencies = [ + "os_str_bytes", +] + +[[package]] +name = "codespan-reporting" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e" +dependencies = [ + "termcolor", + "unicode-width", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc" + +[[package]] +name = "cpufeatures" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28d997bd5e24a5928dd43e46dc529867e207907fe0b239c3477d924f7f2ca320" +dependencies = [ + "libc", +] + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "cxx" +version = "1.0.86" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51d1075c37807dcf850c379432f0df05ba52cc30f279c5cfc43cc221ce7f8579" +dependencies = [ + "cc", + "cxxbridge-flags", + "cxxbridge-macro", + "link-cplusplus", +] + +[[package]] +name = "cxx-build" +version = "1.0.86" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5044281f61b27bc598f2f6647d480aed48d2bf52d6eb0b627d84c0361b17aa70" +dependencies = [ + "cc", + "codespan-reporting", + "once_cell", + "proc-macro2", + "quote", + "scratch", + "syn", +] + +[[package]] +name = "cxxbridge-flags" +version = "1.0.86" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61b50bc93ba22c27b0d31128d2d130a0a6b3d267ae27ef7e4fae2167dfe8781c" + +[[package]] +name = "cxxbridge-macro" +version = "1.0.86" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39e61fda7e62115119469c7b3591fd913ecca96fb766cfd3f2e2502ab7bc87a5" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "deunicode" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "850878694b7933ca4c9569d30a34b55031b9b139ee1fc7b94a527c4ef960d690" + +[[package]] +name = "digest" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8168378f4e5023e7218c89c891c0fd8ecdb5e5e4f18cb78f38cf245dd021e76f" +dependencies = [ + "block-buffer", + "crypto-common", +] + +[[package]] +name = "errno" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f639046355ee4f37944e44f60642c6f3a7efa3cf6b78c78a0d989a8ce6c396a1" +dependencies = [ + "errno-dragonfly", + "libc", + "winapi", +] + +[[package]] +name = "errno-dragonfly" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" +dependencies = [ + "cc", + "libc", +] + +[[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.1.0" source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -39,6 +278,27 @@ dependencies = [ ] [[package]] +name = "generic-array" +version = "0.14.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bff49e947297f3312447abdca79f45f4738097cc82b06e72054d2223f601f1b9" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "getrandom" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c05aeb6a22b8f62540c194aac980f2115af067bfe15a0734d7277a768d396b31" +dependencies = [ + "cfg-if", + "libc", + "wasi 0.11.0+wasi-snapshot-preview1", +] + +[[package]] name = "git2" version = "0.15.0" source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -54,6 +314,75 @@ dependencies = [ ] [[package]] +name = "globset" +version = "0.4.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "029d74589adefde59de1a0c4f4732695c32805624aec7b68d91503d4dba79afc" +dependencies = [ + "aho-corasick", + "bstr", + "fnv", + "log", + "regex", +] + +[[package]] +name = "globwalk" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93e3af942408868f6934a7b85134a3230832b9977cf66125df2f9edcfce4ddcc" +dependencies = [ + "bitflags", + "ignore", + "walkdir", +] + +[[package]] +name = "heck" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9" + +[[package]] +name = "hermit-abi" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7" +dependencies = [ + "libc", +] + +[[package]] +name = "humansize" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02296996cb8796d7c6e3bc2d9211b7802812d36999a51bb754123ead7d37d026" + +[[package]] +name = "iana-time-zone" +version = "0.1.53" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64c122667b287044802d6ce17ee2ddf13207ed924c712de9a66a5814d5b64765" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "wasm-bindgen", + "winapi", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0703ae284fc167426161c2e3f1da3ea71d94b21bedbcc9494e92b28e334e3dca" +dependencies = [ + "cxx", + "cxx-build", +] + +[[package]] name = "idna" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -64,10 +393,60 @@ dependencies = [ ] [[package]] +name = "ignore" +version = "0.4.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a05705bc64e0b66a806c3740bd6578ea66051b157ec42dc219c785cbf185aef3" +dependencies = [ + "globset", + "lazy_static", + "log", + "memchr", + "regex", + "same-file", + "thread_local", + "walkdir", + "winapi-util", +] + +[[package]] +name = "io-lifetimes" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46112a93252b123d31a119a8d1a1ac19deac4fac6e0e8b0df58f0d4e5870e63c" +dependencies = [ + "libc", + "windows-sys", +] + +[[package]] +name = "is-terminal" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28dfb6c8100ccc63462345b67d1bbc3679177c75ee4bf59bf29c8b1d110b8189" +dependencies = [ + "hermit-abi", + "io-lifetimes", + "rustix", + "windows-sys", +] + +[[package]] +name = "itoa" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fad582f4b9e86b6caa621cabeb0963332d92eea04729ab12892c2533951e6440" + +[[package]] name = "itsy-gitsy" version = "0.1.0" dependencies = [ + "chrono", + "clap", "git2", + "serde", + "tera", + "toml", ] [[package]]
@@ -80,6 +459,21 @@ dependencies = [ ] [[package]] +name = "js-sys" +version = "0.3.60" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49409df3e3bf0856b916e2ceaca09ee28e6871cf7d9ce97a692cacfdb2a25a47" +dependencies = [ + "wasm-bindgen", +] + +[[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.139" source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -126,6 +520,21 @@ dependencies = [ ] [[package]] +name = "link-cplusplus" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecd207c9c713c34f95a097a5b029ac2ce6010530c7b49d7fea24d977dede04f5" +dependencies = [ + "cc", +] + +[[package]] +name = "linux-raw-sys" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f051f77a7c8e6957c0696eac88f26b0117e54f52d3fc682ab19397a8812846a4" + +[[package]] name = "log" version = "0.4.17" source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -135,6 +544,37 @@ dependencies = [ ] [[package]] +name = "memchr" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" + +[[package]] +name = "num-integer" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" +dependencies = [ + "autocfg", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" +dependencies = [ + "autocfg", +] + +[[package]] +name = "once_cell" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f61fba1741ea2b3d6a1e3178721804bb716a68a6aeba1149b5d52e3d464ea66" + +[[package]] name = "openssl-probe" version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -154,49 +594,514 @@ dependencies = [ ] [[package]] +name = "os_str_bytes" +version = "6.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b7820b9daea5457c9f21c69448905d723fbd21136ccf521748f23fd49e723ee" + +[[package]] +name = "parse-zoneinfo" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c705f256449c60da65e11ff6626e0c16a0a0b96aaa348de61376b249bc340f41" +dependencies = [ + "regex", +] + +[[package]] name = "percent-encoding" version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e" [[package]] -name = "pkg-config" -version = "0.3.26" +name = "pest" +version = "2.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ac9a59f73473f1b8d852421e59e64809f025994837ef743615c6d0c5b305160" +checksum = "0f6e86fb9e7026527a0d46bc308b841d73170ef8f443e1807f6ef88526a816d4" +dependencies = [ + "thiserror", + "ucd-trie", +] [[package]] -name = "tinyvec" -version = "1.6.0" +name = "pest_derive" +version = "2.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" +checksum = "96504449aa860c8dcde14f9fba5c58dc6658688ca1fe363589d6327b8662c603" dependencies = [ - "tinyvec_macros", + "pest", + "pest_generator", ] [[package]] -name = "tinyvec_macros" -version = "0.1.0" +name = "pest_generator" +version = "2.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" +checksum = "798e0220d1111ae63d66cb66a5dcb3fc2d986d520b98e49e1852bfdb11d7c5e7" +dependencies = [ + "pest", + "pest_meta", + "proc-macro2", + "quote", + "syn", +] [[package]] -name = "unicode-bidi" -version = "0.3.8" +name = "pest_meta" +version = "2.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "099b7128301d285f79ddd55b9a83d5e6b9e97c92e0ea0daebee7263e932de992" +checksum = "984298b75898e30a843e278a9f2452c31e349a073a0ce6fd950a12a74464e065" +dependencies = [ + "once_cell", + "pest", + "sha1", +] [[package]] -name = "unicode-normalization" -version = "0.1.22" +name = "phf" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" +checksum = "928c6535de93548188ef63bb7c4036bd415cd8f36ad25af44b9789b2ee72a48c" dependencies = [ - "tinyvec", + "phf_shared", ] [[package]] -name = "url" +name = "phf_codegen" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a56ac890c5e3ca598bbdeaa99964edb5b0258a583a9eb6ef4e89fc85d9224770" +dependencies = [ + "phf_generator", + "phf_shared", +] + +[[package]] +name = "phf_generator" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1181c94580fa345f50f19d738aaa39c0ed30a600d95cb2d3e23f94266f14fbf" +dependencies = [ + "phf_shared", + "rand", +] + +[[package]] +name = "phf_shared" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1fb5f6f826b772a8d4c0394209441e7d37cbbb967ae9c7e0e8134365c9ee676" +dependencies = [ + "siphasher", + "uncased", +] + +[[package]] +name = "pkg-config" +version = "0.3.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ac9a59f73473f1b8d852421e59e64809f025994837ef743615c6d0c5b305160" + +[[package]] +name = "ppv-lite86" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + +[[package]] +name = "proc-macro-error" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +dependencies = [ + "proc-macro-error-attr", + "proc-macro2", + "quote", + "syn", + "version_check", +] + +[[package]] +name = "proc-macro-error-attr" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +dependencies = [ + "proc-macro2", + "quote", + "version_check", +] + +[[package]] +name = "proc-macro2" +version = "1.0.49" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57a8eca9f9c4ffde41714334dee777596264c7825420f521abc92b5b5deb63a5" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8856d8364d252a14d474036ea1358d63c9e6965c8e5c1885c18f73d70bff9c7b" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + +[[package]] +name = "regex" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e076559ef8e241f2ae3479e36f97bd5741c0330689e217ad51ce2c76808b868a" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.6.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "456c603be3e8d448b072f410900c09faf164fbce2d480456f50eea6e25f9c848" + +[[package]] +name = "rustix" +version = "0.36.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4feacf7db682c6c329c4ede12649cd36ecab0f3be5b7d74e6a20304725db4549" +dependencies = [ + "bitflags", + "errno", + "io-lifetimes", + "libc", + "linux-raw-sys", + "windows-sys", +] + +[[package]] +name = "ryu" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b4b9743ed687d4b4bcedf9ff5eaa7398495ae14e61cba0a295704edbc7decde" + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "scratch" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddccb15bcce173023b3fedd9436f882a0739b8dfb45e4f6b6002bee5929f61b2" + +[[package]] +name = "serde" +version = "1.0.152" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb7d1f0d3021d347a83e556fc4683dea2ea09d87bccdf88ff5c12545d89d5efb" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.152" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af487d118eecd09402d70a5d72551860e788df87b464af30e5ea6a38c75c541e" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.91" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877c235533714907a8c2464236f5c4b2a17262ef1bd71f38f35ea592c8da6883" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "sha1" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f04293dc80c3993519f2d7f6f511707ee7094fe0c6d3406feb330cdb3540eba3" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "siphasher" +version = "0.3.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7bd3e3206899af3f8b12af284fafc038cc1dc2b41d1b89dd17297221c5d225de" + +[[package]] +name = "slug" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3bc762e6a4b6c6fcaade73e77f9ebc6991b676f88bb2358bddb56560f073373" +dependencies = [ + "deunicode", +] + +[[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.107" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f4064b5b16e03ae50984a5a8ed5d4f8803e6bc1fd170a3cda91a1be4b18e3f5" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "tera" +version = "1.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3df578c295f9ec044ff1c829daf31bb7581d5b3c2a7a3d87419afe1f2531438c" +dependencies = [ + "chrono", + "chrono-tz", + "globwalk", + "humansize", + "lazy_static", + "percent-encoding", + "pest", + "pest_derive", + "rand", + "regex", + "serde", + "serde_json", + "slug", + "unic-segment", +] + +[[package]] +name = "termcolor" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "thiserror" +version = "1.0.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a9cd18aa97d5c45c6603caea1da6628790b37f7a34b6ca89522331c5180fed0" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fb327af4685e4d03fa8cbcf1716380da910eeb2bb8be417e7f9fd3fb164f36f" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[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.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b797afad3f312d1c66a56d11d0316f916356d11bd158fbc6ca6389ff6bf805a" +dependencies = [ + "libc", + "wasi 0.10.0+wasi-snapshot-preview1", + "winapi", +] + +[[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 = "toml" +version = "0.5.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1333c76748e868a4d9d1017b5ab53171dfd095f70c712fdb4653a406547f598f" +dependencies = [ + "serde", +] + +[[package]] +name = "typenum" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" + +[[package]] +name = "ucd-trie" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e79c4d996edb816c91e4308506774452e55e95c3c9de07b6729e17e15a5ef81" + +[[package]] +name = "uncased" +version = "0.9.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09b01702b0fd0b3fadcf98e098780badda8742d4f4a7676615cad90e8ac73622" +dependencies = [ + "version_check", +] + +[[package]] +name = "unic-char-property" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8c57a407d9b6fa02b4795eb81c5b6652060a15a7903ea981f3d723e6c0be221" +dependencies = [ + "unic-char-range", +] + +[[package]] +name = "unic-char-range" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0398022d5f700414f6b899e10b8348231abf9173fa93144cbc1a43b9793c1fbc" + +[[package]] +name = "unic-common" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80d7ff825a6a654ee85a63e80f92f054f904f21e7d12da4e22f9834a4aaa35bc" + +[[package]] +name = "unic-segment" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4ed5d26be57f84f176157270c112ef57b86debac9cd21daaabbe56db0f88f23" +dependencies = [ + "unic-ucd-segment", +] + +[[package]] +name = "unic-ucd-segment" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2079c122a62205b421f499da10f3ee0f7697f012f55b675e002483c73ea34700" +dependencies = [ + "unic-char-property", + "unic-char-range", + "unic-ucd-version", +] + +[[package]] +name = "unic-ucd-version" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96bd2f2237fe450fcd0a1d2f5f4e91711124f7857ba2e964247776ebeeb7b0c4" +dependencies = [ + "unic-common", +] + +[[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.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84a22b9f218b40614adcb3f4ff08b703773ad44fa9423e4e0d346d5db86e4ebc" + +[[package]] +name = "unicode-normalization" +version = "0.1.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "unicode-width" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b" + +[[package]] +name = "url" version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0d68c799ae75762b8c3fe375feb6600ef5602c883c5d21eb51c09f22b83c4643"
@@ -211,3 +1116,174 @@ 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 = "walkdir" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "808cf2735cd4b6866113f648b791c6adc5714537bc222d9347bb203386ffda56" +dependencies = [ + "same-file", + "winapi", + "winapi-util", +] + +[[package]] +name = "wasi" +version = "0.10.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f" + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "wasm-bindgen" +version = "0.2.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eaf9f5aceeec8be17c128b2e93e031fb8a4d469bb9c4ae2d7dc1888b26887268" +dependencies = [ + "cfg-if", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c8ffb332579b0557b52d268b91feab8df3615f265d5270fec2a8c95b17c1142" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "052be0f94026e6cbc75cdefc9bae13fd6052cdcaf532fa6c45e7ae33a1e6c810" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07bc0c051dc5f23e307b13285f9d75df86bfdf816c5721e573dec1f9b8aa193c" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c38c045535d93ec4f0b4defec448e4291638ee608530863b1e2ba115d4fff7f" + +[[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.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d2aa71f6f0cbe00ae5167d90ef3cfe66527d6f613ca78ac8024c3ccab9a19e" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd0f252f5a35cac83d6311b2e795981f5ee6e67eb1f9a7f64eb4500fbc4dcdb4" + +[[package]] +name = "windows_i686_gnu" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbeae19f6716841636c28d695375df17562ca208b2b7d0dc47635a50ae6c5de7" + +[[package]] +name = "windows_i686_msvc" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84c12f65daa39dd2babe6e442988fc329d6243fdce47d7d2d155b8d874862246" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf7b1b21b5362cbc318f686150e5bcea75ecedc74dd157d874d754a2ca44b0ed" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09d525d2ba30eeb3297665bd434a54297e4170c7f1a44cad4ef58095b4cd2028" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f40009d85759725a34da6d89a94e63d7bdc50a862acf0dbc7c8e488f1edcb6f5"
diff --git a/Cargo.toml b/Cargo.toml line changes: +5/-1 index dbff112..89f9634 --- a/Cargo.toml +++ b/Cargo.toml
@@ -11,7 +11,11 @@ repository = "https://github.com/mrmekon/itsy-gitsy" readme = "README.md" license = "GPL-3.0-or-later" -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +chrono = "0.4.23" +clap = { version="4.0.32", features=["derive"] } git2 = "0.15.0" +serde = { version = "1.0.152", features = ["derive"] } +tera = "1.17.1" +toml = "0.5.10"
diff --git a/settings.toml b/settings.toml line changes: +12/-0 index 0000000..b90c15e --- /dev/null +++ b/settings.toml
@@ -0,0 +1,12 @@ +template_dir = "templates/" +output_dir = "gen/" + +[a_repo] +path = "repos/connectr" +name = "connectr" + +[another_repo] +path = "repos/fruitbasket" + +[extra] +thingo = 1
diff --git a/src/main.rs b/src/main.rs line changes: +766/-8 index 7fa12e6..7b8f275 --- a/src/main.rs +++ b/src/main.rs
@@ -1,16 +1,774 @@ -use git2::{Commit, DiffOptions, ObjectType, Repository, Signature, Time}; -use git2::{DiffFormat, Error, Pathspec}; +use chrono::{ + DateTime, + offset::FixedOffset, + naive::NaiveDateTime, +}; +use clap::Parser; +use git2::{DiffOptions, Repository, Error}; +use serde::{Serialize, Deserialize}; +use std::collections::{BTreeMap, HashMap}; +use std::io::Write; +use std::path::{Path, PathBuf}; +use tera::{Context, Filter, Function, Tera, Value, to_value, try_get_value}; + +fn ts_to_date(ts: i64, offset: Option<i64>, format: Option<String>) -> String { + let offset = offset.unwrap_or(0); + let dt = NaiveDateTime::from_timestamp_opt(ts + offset, 0).expect("Invalid timestamp"); + let dt_tz: DateTime<FixedOffset> = DateTime::from_local(dt, FixedOffset::east_opt(offset as i32).expect("Invalid timezone")); + match format { + Some(f) => dt_tz.format(&f).to_string(), + None => dt_tz.format("%Y-%m-%d").to_string(), + } +} + +fn ts_to_git_timestamp(ts: i64, offset: Option<i64>) -> String { + let offset = offset.unwrap_or(0); + let dt = chrono::naive::NaiveDateTime::from_timestamp_opt(ts + offset, 0).expect("invalid timestamp"); + let dt_tz: DateTime<FixedOffset> = DateTime::from_local(dt, FixedOffset::east_opt(offset as i32).expect("Invalid timezone")); + dt_tz.format("%a %b %e %T %Y %z").to_string() +} + +fn first_line(msg: &[u8]) -> String { + let message = String::from_utf8_lossy(msg); + message.lines().next().unwrap_or("[no commit message]").to_owned() +} + +#[derive(Serialize)] +struct GitRepo { + name: String, + metadata: ItsyMetadata, + history: Vec<GitObject>, + branches: Vec<GitObject>, + tags: Vec<GitObject>, + root_files: Vec<GitFile>, + all_files: Vec<GitFile>, + commits: BTreeMap<String, GitObject>, +} + +#[derive(Serialize, Default)] +struct ItsyMetadata { + full_name: Option<String>, + description: Option<String>, + website: Option<String>, + clone: Option<String>, + attributes: BTreeMap<String, String>, +} + +#[derive(Serialize, Default)] +struct GitAuthor { + name: Option<String>, + email: Option<String>, +} + +#[derive(Serialize, Default)] +struct GitObject { + full_hash: String, + short_hash: String, + ts_utc: i64, + ts_offset: i64, + author: GitAuthor, + committer: GitAuthor, + parents: Vec<String>, + ref_name: Option<String>, + alt_refs: Vec<String>, + tagged_id: Option<String>, + tree_id: Option<String>, + summary: Option<String>, + message: Option<String>, + stats: Option<GitStats>, + diff: Option<GitDiffCommit>, +} + +#[derive(Serialize, Default)] +struct GitStats { + files: usize, + additions: usize, + deletions: usize, +} + +#[derive(Serialize, Deserialize, Clone)] +struct GitFile { + id: String, + name: String, + path: String, + mode: i32, + kind: String, + is_binary: bool, + size: usize, + contents: Option<String>, +} + +#[derive(Serialize, Default)] +struct GitDiffCommit { + files: Vec<GitDiffFile>, + file_count: usize, + additions: usize, + deletions: usize, +} + +#[derive(Serialize, Default)] +struct GitDiffFile { + oldfile: String, + newfile: String, + basefile: String, + oldid: String, + newid: String, + extra: String, + additions: usize, + deletions: usize, + hunks: Vec<GitDiffHunk> +} + +#[derive(Serialize, Default)] +struct GitDiffHunk { + context: String, + lines: Vec<GitDiffLine>, +} + +#[derive(Serialize)] +struct GitDiffLine { + kind: &'static str, + prefix: &'static str, + text: String, +} + +fn walk_file_tree(repo: &git2::Repository, rev: &str, files: &mut Vec<GitFile>, + depth: usize, recurse: bool, prefix: &str) -> Result<(), Error> { + let obj = repo.revparse_single(rev)?; + let tree = obj.peel_to_tree()?; + for entry in tree.iter() { + let name = prefix.to_string() + entry.name().unwrap_or_default(); + let kind = match entry.kind() { + Some(git2::ObjectType::Tree) => "dir", + Some(git2::ObjectType::Blob) => "file", + Some(git2::ObjectType::Commit) => "submodule", + _ => "unknown", + }; + let mut is_binary = false; + let mut size = 0; + + if let Ok(blob) = repo.find_blob(entry.id()) { + is_binary = blob.is_binary(); + size = blob.content().len(); + } + files.push(GitFile { + id: entry.id().to_string(), + name: name.clone(), + path: match depth { + 0 => name.to_string(), + _ => format!("{}/{}", prefix, name), + }, + kind: kind.to_string(), + mode: entry.filemode(), + is_binary, + size, + contents: None, + }); + if recurse && entry.kind() == Some(git2::ObjectType::Tree) { + let prefix = name + "/"; + walk_file_tree(repo, &entry.id().to_string(), files, depth+1, true, &prefix)?; + } + } + Ok(()) +} + +fn parse_repo(repo: &Repository, name: &str) -> Result<GitRepo, Error> { + let mut history: Vec<GitObject> = vec!(); + let mut branches: Vec<GitObject> = vec!(); + let mut tags: Vec<GitObject> = vec!(); + let mut commits: BTreeMap<String, GitObject> = BTreeMap::new(); -fn run() -> Result<(), Error> { - let path = "."; - let repo = Repository::open(path)?; let mut revwalk = repo.revwalk()?; revwalk.set_sorting(git2::Sort::TOPOLOGICAL)?; revwalk.push_head()?; - Ok(()) + for oid in revwalk { + let oid = oid?; + commits.insert(oid.to_string(), parse_commit(repo, &oid.to_string())?); + let commit = repo.find_commit(oid)?; + let obj = repo.revparse_single(&commit.id().to_string())?; + let full_hash = commit.id().to_string(); + let short_hash = obj.short_id()?.as_str().unwrap_or_default().to_string(); + + let mut parents: Vec<String> = vec!(); + let a = if commit.parents().len() == 1 { + let parent = commit.parent(0)?; + parents.push(parent.id().to_string()); + Some(parent.tree()?) + } else { + None + }; + let b = commit.tree()?; + let mut diffopts = DiffOptions::new(); + let diff = repo.diff_tree_to_tree(a.as_ref(), Some(&b), Some(&mut diffopts))?; + let stats = diff.stats()?; + let stats = GitStats { + files: stats.files_changed(), + additions: stats.insertions(), + deletions: stats.deletions(), + }; + + // TODO: is it acceptable to iterate over all references for + // every commit? Is there another way? Should probably cache + // all of the ref IDs in memory. + let mut alt_refs = vec!(); + for refr in repo.references()? { + let refr = refr?; + if let Some(target) = refr.target() { + if target == commit.id() { + // TODO: save these + if let Some(name) = refr.shorthand() { + alt_refs.push(name.to_string()); + } + } + } + } + + history.push(GitObject { + full_hash, + short_hash, + ts_utc: commit.author().when().seconds(), + ts_offset: (commit.author().when().offset_minutes() as i64) * 60, + parents, + ref_name: None, + alt_refs, + author: GitAuthor { + name: commit.author().name().map(|x| x.to_owned()), + email: commit.author().email().map(|x| x.to_owned()), + }, + summary: Some(first_line(commit.message_bytes())), + stats: Some(stats), + ..Default::default() + }); + } + + for branch in repo.branches(None)? { + let (branch, _branch_type) = branch?; + let refr = branch.get(); + let name = branch.name()?.unwrap_or("[unnamed]"); + let obj = repo.revparse_single(name)?; + // Only show direct references, skip symbolic aliases. Maybe + // this is a bad idea? + match refr.kind() { + Some(k) if k == git2::ReferenceType::Symbolic => continue, + _ => {}, + } + let commit = repo.find_commit(obj.id())?; + let full_hash = obj.id().to_string(); + let short_hash = obj.short_id()?.as_str().unwrap_or_default().to_string(); + branches.push(GitObject { + full_hash, + short_hash, + ts_utc: commit.author().when().seconds(), + ts_offset: (commit.author().when().offset_minutes() as i64) * 60, + parents: vec!(), + ref_name: Some(name.to_string()), + author: GitAuthor { + name: commit.author().name().map(|x| x.to_owned()), + email: commit.author().email().map(|x| x.to_owned()), + }, + committer: GitAuthor { + name: commit.committer().name().map(|x| x.to_owned()), + email: commit.committer().email().map(|x| x.to_owned()), + }, + summary: Some(first_line(commit.message_bytes())), + message: commit.message().map(|x| x.to_string()), + ..Default::default() + }); + } + for tag in repo.tag_names(None)?.iter() { + let tag = tag.unwrap_or("[unnamed]"); + let obj = repo.revparse_single(tag)?; + let commit = repo.find_tag(obj.id())?; + let full_hash = obj.id().to_string(); + let short_hash = obj.short_id()?.as_str().unwrap_or_default().to_string(); + let (ts, tz) = match commit.tagger() { + Some(t) => (t.when().seconds(), (t.when().offset_minutes() as i64) * 60), + _ => (0, 0), + }; + let (author,email) = match commit.tagger() { + Some(t) => (t.name().map(|x| x.to_owned()), + t.email().map(|x| x.to_owned())), + _ => (None, None), + }; + let summary = match commit.message_bytes() { + Some(m) => Some(first_line(m)), + _ => None, + }; + tags.push(GitObject { + full_hash, + short_hash, + ts_utc: ts, + ts_offset: tz, + ref_name: Some(tag.to_string()), + author: GitAuthor { + name: author, + email, + }, + tagged_id: Some(commit.target_id().to_string()), + message: commit.message().map(|x| x.to_string()), + summary, + ..Default::default() + }); + } + + let mut root_files: Vec<GitFile> = vec!(); + let mut all_files: Vec<GitFile> = vec!(); + walk_file_tree(&repo, "origin/HEAD", &mut root_files, 0, false, "")?; + // TODO: maybe this should be optional? Walking the whole tree + // could be slow on huge repos. + walk_file_tree(&repo, "origin/HEAD", &mut all_files, 0, true, "")?; + + Ok(GitRepo { + name: name.to_string(), + metadata: Default::default(), + history, + branches, + tags, + root_files, + all_files, + commits, + }) +} + +fn parse_commit(repo: &Repository, refr: &str) -> Result<GitObject, Error> { + let obj = repo.revparse_single(refr)?; + let commit = repo.find_commit(obj.id())?; + let mut parents: Vec<String> = vec!(); + + let a = match commit.parents().len() { + x if x == 1 => { + let parent = commit.parent(0).unwrap(); + parents.push(parent.id().to_string()); + Some(parent.tree()?) + }, + x if x > 1 => { + for parent in commit.parents() { + parents.push(parent.id().to_string()); + } + None + }, + _ => { + None + }, + }; + let b = commit.tree()?; + let mut diffopts = DiffOptions::new(); + let diff = repo.diff_tree_to_tree(a.as_ref(), Some(&b), Some(&mut diffopts))?; + let stats = diff.stats()?; + + let mut commit_diff: GitDiffCommit = GitDiffCommit { + file_count: stats.files_changed(), + additions: stats.insertions(), + deletions: stats.deletions(), + ..Default::default() + }; + let files: std::rc::Rc<std::cell::RefCell<Vec<GitDiffFile>>> = std::rc::Rc::new(std::cell::RefCell::new(vec!())); + + diff.foreach( + &mut |file, _progress| { + let mut file_diff: GitDiffFile = Default::default(); + file_diff.newfile = match file.status() { + git2::Delta::Deleted => "/dev/null".to_owned(), + _ => file.new_file().path().map(|x| "b/".to_string() + &x.to_string_lossy()).unwrap_or("/dev/null".to_string()), + }; + file_diff.oldfile = match file.status() { + git2::Delta::Added => "/dev/null".to_owned(), + _ => file.old_file().path().map(|x| "a/".to_string() + &x.to_string_lossy()).unwrap_or("/dev/null".to_string()), + }; + file_diff.basefile = match file.status() { + git2::Delta::Added => file.new_file().path().map(|x| x.to_string_lossy().to_string()).unwrap_or("/dev/null".to_string()), + _ => file.old_file().path().map(|x| x.to_string_lossy().to_string()).unwrap_or("/dev/null".to_string()), + }; + file_diff.oldid = file.old_file().id().to_string(); + file_diff.newid = file.new_file().id().to_string(); + files.borrow_mut().push(file_diff); + true + }, + None, // TODO: handle binary files? + Some(&mut |_file, hunk| { + let mut files = files.borrow_mut(); + let file_diff: &mut GitDiffFile = files.last_mut().expect("Diff hunk not associated with a file!"); + let mut hunk_diff: GitDiffHunk = Default::default(); + hunk_diff.context = String::from_utf8_lossy(hunk.header()).to_string(); + file_diff.hunks.push(hunk_diff); + true + }), + Some(&mut |_file, _hunk, line| { + let mut files = files.borrow_mut(); + let file_diff: &mut GitDiffFile = files.last_mut().expect("Diff hunk not associated with a file!"); + let hunk_diff: &mut GitDiffHunk = file_diff.hunks.last_mut().expect("Diff line not associated with a hunk!"); + let (kind, prefix) = match line.origin() { + ' ' => ("ctx", " "), + '-' => ("del", "-"), + '+' => ("add", "+"), + _ => ("other", " "), + }; + match line.origin() { + '-' => file_diff.deletions += 1, + '+' => file_diff.additions += 1, + _ => {}, + } + let line_diff = GitDiffLine { + text: String::from_utf8_lossy(line.content()).to_string(), + kind, + prefix, + }; + hunk_diff.lines.push(line_diff); + true + }) + )?; + + match std::rc::Rc::try_unwrap(files) { + Ok(files) => { + let files: Vec<GitDiffFile> = files.into_inner(); + commit_diff.files = files; + }, + Err(_) => {}, + } + + let tree = obj.peel_to_tree()?; + let summary = GitObject { + full_hash: obj.id().to_string(), + short_hash: obj.short_id()?.as_str().unwrap_or_default().to_string(), + ts_utc: commit.author().when().seconds(), + ts_offset: (commit.author().when().offset_minutes() as i64) * 60, + tagged_id: None, + tree_id: Some(tree.id().to_string()), + parents, + ref_name: None, + alt_refs: vec!(), + author: GitAuthor { + name: commit.author().name().map(|x| x.to_string()), + email: commit.author().email().map(|x| x.to_string()), + }, + committer: GitAuthor { + name: commit.committer().name().map(|x| x.to_string()), + email: commit.committer().email().map(|x| x.to_string()), + }, + summary: Some(first_line(commit.message_bytes())), + message: commit.message().map(|x| x.to_string()), + stats: None, + diff: Some(commit_diff), + }; + + Ok(summary) +} + +fn fill_file_contents(repo: &Repository, file: &GitFile) -> Result<GitFile, Error> { + let mut file = file.clone(); + if file.kind == "file" { + let blob = repo.find_blob(git2::Oid::from_str(&file.id)?)?; + file.contents = match blob.is_binary() { + false => Some(String::from_utf8_lossy(blob.content()).to_string()), + true => Some(format!("[Binary data ({} bytes)]", blob.content().len())), + }; + } + Ok(file) +} + +fn dir_listing(repo: &Repository, file: &GitFile) -> Result<Vec<GitFile>, Error> { + let mut files: Vec<GitFile> = vec!(); + walk_file_tree(&repo, &file.id, &mut files, 0, false, "")?; + Ok(files) +} + +struct FileFilter; +impl Filter for FileFilter { + fn filter(&self, value: &Value, _args: &HashMap<String, Value> + ) -> Result<Value, tera::Error> { + let file_list: Vec<GitFile> = try_get_value!("only_files", "value", Vec<GitFile>, value); + let file_list: Vec<GitFile> = file_list.iter().filter_map(|x| match x.kind.as_str() { + "file" => Some(x.clone()), + _ => None, + }).collect(); + Ok(to_value(file_list).unwrap()) + } +} + +struct DirFilter; +impl Filter for DirFilter { + fn filter(&self, value: &Value, _args: &HashMap<String, Value> + ) -> Result<Value, tera::Error> { + let file_list: Vec<GitFile> = try_get_value!("only_dirs", "value", Vec<GitFile>, value); + let file_list: Vec<GitFile> = file_list.iter().filter_map(|x| match x.kind.as_str() { + "dir" => Some(x.clone()), + _ => None, + }).collect(); + Ok(to_value(file_list).unwrap()) + } +} + +struct TsDateFn; +impl Function for TsDateFn { + fn call(&self, args: &HashMap<String, Value>) -> Result<Value, tera::Error> { + let ts: Option<i64> = match args.get("ts") { + Some(ts) => match tera::from_value(ts.clone()) { + Ok(ts) => Some(ts), + _ => None, + }, + _ => None, + }; + let ts = ts.expect("ts_to_date missing a `ts` argument"); + + let tz: Option<i64> = match args.get("tz") { + Some(tz) => match tera::from_value(tz.clone()) { + Ok(tz) => Some(tz), + _ => None, + }, + _ => None, + }; + + let fmt: Option<String> = match args.get("fmt") { + Some(fmt) => match tera::from_value(fmt.clone()) { + Ok(fmt) => Some(fmt), + _ => None, + }, + _ => None, + }; + Ok(to_value(ts_to_date(ts, tz, fmt)).unwrap()) + } +} + +struct TsTimestampFn; +impl Function for TsTimestampFn { + fn call(&self, args: &HashMap<String, Value>) -> Result<Value, tera::Error> { + let ts: Option<i64> = match args.get("ts") { + Some(ts) => match tera::from_value(ts.clone()) { + Ok(ts) => Some(ts), + _ => None, + }, + _ => None, + }; + let ts = ts.expect("ts_to_git_timestamp missing a `ts` argument"); + + let tz: Option<i64> = match args.get("tz") { + Some(tz) => match tera::from_value(tz.clone()) { + Ok(tz) => Some(tz), + _ => None, + }, + _ => None, + }; + Ok(to_value(ts_to_git_timestamp(ts, tz)).unwrap()) + } +} + +#[derive(Parser, Debug)] +#[command(author, version, about, long_about = None)] +struct CliArgs { + #[arg(short, long, value_name = "FILE")] + config: Option<PathBuf>, +} + +#[derive(Deserialize)] +#[allow(dead_code)] +struct ItsySettings { + template_dir: PathBuf, + output_dir: PathBuf, + recursive_repo_dirs: Option<Vec<PathBuf>>, + extra: HashMap<String, toml::Value>, +} +#[derive(Deserialize)] +#[allow(dead_code)] +struct ItsySettingsRepo { + path: PathBuf, + name: Option<String>, + description: Option<String>, + website: Option<String>, } fn main() { - println!("Hello, world!"); - run().expect("run failed"); + let cli = CliArgs::parse(); + let config_path = cli.config.as_deref().unwrap_or(Path::new("config.toml")); + + // Parse the known settings directly into their struct + let toml = std::fs::read_to_string(config_path).expect(&format!("Configuration file not found: {}", config_path.display())); + let settings: ItsySettings = toml::from_str(&toml).expect("Configuration file is invalid."); + + // Get a list of all remaining TOML "tables" in the file. + // These are the user-supplied individual repositories. + let reserved_keys = vec!("repos","extra"); + let settings_raw: HashMap<String, toml::Value> = toml::from_str(&toml).expect("blah"); + let table_keys: Vec<String> = settings_raw.iter().filter_map(|x| match x.1.is_table() { + true => match reserved_keys.contains(&x.0.as_str()) { + false => Some(x.0.clone()), + true => None, + }, + false => None + }).collect(); + + // Try to convert each unknown "table" into a repo struct, and + // save the ones that are successful. If no repo name is + // specified, use the TOML table name. + let mut repos: Vec<ItsySettingsRepo> = vec!(); + for k in &table_keys { + let v = settings_raw.get(k).unwrap(); + match toml::from_str::<ItsySettingsRepo>(&v.to_string()) { + Ok(mut repo) => { + if repo.name.is_none() { + repo.name = Some(k.clone()); + } + repos.push(repo); + }, + _ => {}, + } + } + + for repo in &repos { + println!("Parse repo: {}", repo.name.as_ref().unwrap()); + } + + let mut template_path = settings.template_dir.clone(); + template_path.push("**"); + template_path.push("*.html"); + let mut tera = match Tera::new(template_path.to_str().expect("No template path set!")) { + Ok(t) => t, + Err(e) => { + println!("Parsing error(s): {}", e); + ::std::process::exit(1); + } + }; + tera.register_filter("only_files", FileFilter{}); + tera.register_filter("only_dirs", DirFilter{}); + tera.register_function("ts_to_date", TsDateFn{}); + tera.register_function("ts_to_git_timestamp", TsTimestampFn{}); + + // Create output directory + let _ = std::fs::create_dir(settings.output_dir.to_str().expect("Output path not set!")); + + let mut repos: Vec<GitRepo> = vec!(); + for dir in std::fs::read_dir(std::path::Path::new("repos")).expect("Repo directory not found.") { + let dir = dir.expect("Repo contains invalid entries"); + match dir.metadata() { + Ok(m) if m.is_dir() => {}, + _ => continue, + } + let path: String = dir.path().to_string_lossy().to_string(); + let name: String = dir.file_name().to_string_lossy().to_string(); + let repo = Repository::open(path).expect("Unable to find git repository."); + let summary = parse_repo(&repo, &name).expect("Failed to analyze repo HEAD."); + + let mut local_ctx = Context::from_serialize(&summary).unwrap(); + match tera.render("summary.html", &local_ctx) { + Ok(rendered) => { + let mut output_path = settings.output_dir.clone(); + output_path.push(&name); + let _ = std::fs::create_dir(output_path.to_str().expect("Output path not set!")); + output_path.push("summary.html"); + let mut file = std::fs::File::create(output_path.to_str().expect("Output path not set!")).unwrap(); + file.write(rendered.as_bytes()).expect("failed to save rendered html"); + }, + Err(x) => println!("ERROR: {:?}", x), + } + + for branch in &summary.branches { + local_ctx.insert("branch", branch); + match tera.render("branch.html", &local_ctx) { + Ok(rendered) => { + let mut output_path = settings.output_dir.clone(); + output_path.push(&summary.name); + output_path.push("branch"); + let _ = std::fs::create_dir(output_path.to_str().expect("Output path not set!")); + output_path.push(format!("{}.html", branch.full_hash)); + let mut file = std::fs::File::create(output_path.to_str().expect("Output path not set!")).unwrap(); + file.write(rendered.as_bytes()).expect("failed to save rendered html"); + }, + Err(x) => match x.kind { + tera::ErrorKind::TemplateNotFound(_) => {}, + _ => println!("ERROR: {:?}", x), + }, + } + local_ctx.remove("branch"); + } + + for tag in &summary.tags { + local_ctx.insert("tag", tag); + if let Some(commit) = summary.commits.get(tag.tagged_id.as_ref().unwrap()) { + local_ctx.insert("commit", &commit); + } + match tera.render("tag.html", &local_ctx) { + Ok(rendered) => { + let mut output_path = settings.output_dir.clone(); + output_path.push(&summary.name); + output_path.push("tag"); + let _ = std::fs::create_dir(output_path.to_str().expect("Output path not set!")); + output_path.push(format!("{}.html", tag.full_hash)); + let mut file = std::fs::File::create(output_path.to_str().expect("Output path not set!")).unwrap(); + file.write(rendered.as_bytes()).expect("failed to save rendered html"); + }, + Err(x) => match x.kind { + tera::ErrorKind::TemplateNotFound(_) => {}, + _ => println!("ERROR: {:?}", x), + }, + } + local_ctx.remove("tag"); + local_ctx.remove("commit"); + } + + for (_id, commit) in &summary.commits { + local_ctx.try_insert("commit", &commit).expect("Failed to add commit to template engine."); + match tera.render("commit.html", &local_ctx) { + Ok(rendered) => { + let mut output_path = settings.output_dir.clone(); + output_path.push(&summary.name); + output_path.push("commit"); + let _ = std::fs::create_dir(output_path.to_str().expect("Output path not set!")); + output_path.push(format!("{}.html", commit.full_hash)); + let mut file = std::fs::File::create(output_path.to_str().expect("Output path not set!")).unwrap(); + file.write(rendered.as_bytes()).expect("failed to save rendered html"); + }, + Err(x) => println!("ERROR: {:?}", x), + } + local_ctx.remove("commit"); + } + + for file in summary.all_files.iter().filter(|x| x.kind == "file") { + let file = fill_file_contents(&repo, &file).expect("Failed to parse file."); + local_ctx.try_insert("file", &file).expect("Failed to add file to template engine."); + match tera.render("file.html", &local_ctx) { + Ok(rendered) => { + let mut output_path = settings.output_dir.clone(); + output_path.push(&summary.name); + output_path.push("file"); + let _ = std::fs::create_dir(output_path.to_str().expect("Output path not set!")); + output_path.push(format!("{}.html", file.id)); + let mut file = std::fs::File::create(output_path.to_str().expect("Output path not set!")).unwrap(); + file.write(rendered.as_bytes()).expect("failed to save rendered html"); + }, + Err(x) => println!("ERROR: {:?}", x), + } + local_ctx.remove("file"); + } + + for dir in summary.all_files.iter().filter(|x| x.kind == "dir") { + let listing = dir_listing(&repo, &dir).expect("Failed to parse file."); + local_ctx.try_insert("files", &listing).expect("Failed to add dir to template engine."); + match tera.render("dir.html", &local_ctx) { + Ok(rendered) => { + let mut output_path = settings.output_dir.clone(); + output_path.push(&summary.name); + output_path.push("dir"); + let _ = std::fs::create_dir(output_path.to_str().expect("Output path not set!")); + output_path.push(format!("{}.html", dir.id)); + let mut file = std::fs::File::create(output_path.to_str().expect("Output path not set!")).unwrap(); + file.write(rendered.as_bytes()).expect("failed to save rendered html"); + }, + Err(x) => println!("ERROR: {:?}", x), + } + local_ctx.remove("files"); + } + + repos.push(summary); + } + + let mut global_ctx = Context::new(); + global_ctx.try_insert("repos", &repos).expect("Failed to add repo to template engine."); + match tera.render("repos.html", &global_ctx) { + Ok(rendered) => { + let mut output_path = settings.output_dir.clone(); + output_path.push("repos.html"); + let mut file = std::fs::File::create(output_path.to_str().expect("Output path not set!")).unwrap(); + file.write(rendered.as_bytes()).expect("failed to save rendered html"); + }, + Err(x) => println!("ERROR: {:?}", x), + } }
diff --git a/templates/branch.html b/templates/branch.html line changes: +11/-0 index 0000000..f979a62 --- /dev/null +++ b/templates/branch.html
@@ -0,0 +1,11 @@ +<html> + <body style="font-family: monospace;"> + branch: {{branch.ref_name}}<br/> + hash: {{branch.full_hash}} ({{branch.short_hash}})<br/> + author: {{branch.author.name}}<br/> + committer: {{branch.committer.name}}<br/> + date: {{ts_to_date(ts=branch.ts_utc, tz=branch.ts_offset)}}<br/> + summary: {{branch.summary}}<br/> + <pre>{{branch.message}}</pre> + </body> +</html>
diff --git a/templates/commit.html b/templates/commit.html line changes: +31/-0 index 0000000..c216f9c --- /dev/null +++ b/templates/commit.html
@@ -0,0 +1,31 @@ +<html> + <body style="font-family: monospace;"> + <strong> + commit: {{commit.full_hash}}<br/> + author: {{commit.author.name}}<br/> + committer: {{commit.committer.name}}<br/> + parent: {% if commit.parents | length > 0 -%}<a href="{{commit.parents | first}}.html">{{commit.parents | first}}</a>{%-endif-%}<br/> + </strong> + <br/> + <pre style="margin: 0;">{{commit.message}}</pre> + {% for file in commit.diff.files -%} + <br/> + <strong> + diff --git a/{{file.basefile}} b/{{file.basefile}}<br/> + line changes: +{{file.additions}}/-{{file.deletions}}<br/> + index {{file.oldid | truncate(length=7,end="")}}..{{file.newid | truncate(length=7,end="")}}<br/> + --- {{file.oldfile}}<br/> + +++ {{file.newfile}} + </strong> + {% for hunk in file.hunks -%} + <pre><strong>{{hunk.context}}</strong> + {%- for line in hunk.lines -%} + {%- if line.kind in ["del","add"] -%}<span style="color: {%- if line.kind == "del" -%}bb0000{%- else -%}00aa00{% endif %}">{%- endif -%} + {{line.prefix}}{{line.text}} + {%- if line.kind in ["del","add"] -%}</span>{%- endif -%} + {%- endfor -%} + </pre> + {% endfor -%} + {% endfor -%} +</body> +</html>
diff --git a/templates/dir.html b/templates/dir.html line changes: +21/-0 index 0000000..10a0eb3 --- /dev/null +++ b/templates/dir.html
@@ -0,0 +1,21 @@ +<html> + <body style="font-family: monospace;"> + <table class="files"> + <tr> + <th>File</th> + <th>ID</th> + <th>Type</th> + <th>Mode</th> + <th>Size</th> + </tr> + {% for file in files -%} + <tr class="file"> + <td class="file-name"><a href="../{{file.kind}}/{{file.id}}.html">{{file.name}}</a></td> + <td class="file-id">{{file.id}}</td> + <td class="file-type">{{file.kind}} ({{file.is_binary}})</td> + <td class="file-mode">{{file.mode}}</td> + <td class="file-size">{{file.size}}</td> + </tr> + {% endfor -%} + </body> +</html>
diff --git a/templates/file.html b/templates/file.html line changes: +7/-0 index 0000000..762aa06 --- /dev/null +++ b/templates/file.html
@@ -0,0 +1,7 @@ +<html> + <body style="font-family: monospace;"> + {{file.path}} ({{file.name}}) [{{file.id}}]<br/> + ------- + <pre style="margin: 0">{{file.contents}}</pre> + </body> +</html>
diff --git a/templates/repos.html b/templates/repos.html line changes: +7/-0 index 0000000..1ce812d --- /dev/null +++ b/templates/repos.html
@@ -0,0 +1,7 @@ +<html> + <body style="font-family: monospace;"> + {% for repo in repos -%} + <a href="{{repo.name}}/summary.html">{{ repo.name }}</a> ({{ts_to_date(ts=repo.history[0].ts_utc, tz=repo.history[0].ts_offset)}})<br/> + {% endfor -%} + </body> +</html>
diff --git a/templates/summary.html b/templates/summary.html line changes: +83/-0 index 0000000..70377f3 --- /dev/null +++ b/templates/summary.html
@@ -0,0 +1,83 @@ +<html> + <body style="font-family: monospace;"> + <table class="commits"> + <tr> + <th>Commit ID</th> + <th>Message</th> + <th>Author</th> + <th>Date</th> + <th>Diff</th> + <th>Refs</th> + </tr> + {% for entry in history -%} + {% if loop.index0 < 250 -%} + <tr class="commit"> + <td class="oid"><a href="commit/{{entry.full_hash}}.html">{{entry.short_hash}}</a></td> + <td class="commit-msg" style="font-family: sans-serif;">{{entry.summary}}</td> + <td class="author" style="font-family: sans-serif;">{{entry.author.name}}</td> + <td class="date">{{ts_to_date(ts=entry.ts_utc, tz=entry.ts_offset)}}</td> + <td class="diff">{{entry.stats.files}} (+{{entry.stats.additions}}/-{{entry.stats.deletions}})</td> + <td class="refs">{%- for ref in entry.alt_refs -%}{%- if loop.index0 > 0 -%}, {%- endif -%}<span class="commit-ref">{{ref}}</span>{%- endfor -%}</td> + </tr> + {% endif -%} + {% endfor -%} + </table> + + <table class="branches"> + <tr> + <th>Branch</th> + <th>Commit ID</th> + <th>Message</th> + <th>Author</th> + <th>Date</th> + </tr> + {% for entry in branches -%} + <tr class="branch"> + <td class="name"><a href="branch/{{entry.full_hash}}.html">{{entry.ref_name}}</a></td> + <td class="oid">{{entry.short_hash}}</td> + <td class="commit-msg" style="font-family: sans-serif;">{{entry.summary}}</td> + <td class="author">{{entry.author.name}}</td> + <td class="date">{{ts_to_date(ts=entry.ts_utc, tz=entry.ts_offset)}}</td> + </tr> + {% endfor -%} + </table> + + <table class="tags"> + <tr> + <th>Tag</th> + <th>Commit ID</th> + <th>Message</th> + <th>Author</th> + <th>Date</th> + </tr> + {% for entry in tags -%} + <tr class="tag"> + <td class="name"><a href="tag/{{entry.full_hash}}.html">{{entry.ref_name}}</a></td> + <td class="oid">{{entry.short_hash}}</td> + <td class="commit-msg" style="font-family: sans-serif;">{{entry.summary}}</td> + <td class="author">{{entry.author.name}}</td> + <td class="date">{{ts_to_date(ts=entry.ts_utc, tz=entry.ts_offset)}}</td> + </tr> + {% endfor -%} + </table> + + <table class="files"> + <tr> + <th>File</th> + <th>ID</th> + <th>Type</th> + <th>Mode</th> + <th>Size</th> + </tr> + {% for file in root_files -%} + <tr class="file"> + <td class="name"><a href="{{file.kind}}/{{file.id}}.html">{{file.name}}</a></td> + <td class="id">{{file.id}}</td> + <td class="type">{{file.kind}} ({{file.is_binary}})</td> + <td class="mode">{{file.mode}}</td> + <td class="size">{{file.size}}</td> + </tr> + {% endfor -%} + </table> +</body> +</html>
diff --git a/templates/tag.html b/templates/tag.html line changes: +15/-0 index 0000000..53bf17e --- /dev/null +++ b/templates/tag.html
@@ -0,0 +1,15 @@ +<html> + <body style="font-family: monospace;"> + branch: {{tag.ref_name}}<br/> + hash: {{tag.full_hash}} ({{tag.short_hash}})<br/> + author: {{tag.author.name}}<br/> + committer: {{tag.committer.name}}<br/> + date: {{ts_to_date(ts=tag.ts_utc, tz=tag.ts_offset)}}<br/> + summary: {{tag.summary}}<br/> + <pre>{{tag.message}}</pre> + <br/> + commit: {%- if commit -%}<a href="../commit/{{commit.full_hash}}.html">{{commit.full_hash}}</a> + <pre>{{commit.message}}</pre> + {%-else-%}{{tag.tagged_id}}{%-endif-%}<br/> + </body> +</html>