From e348b79e7b38228d3ca986a76e3969f8038b2d69 Mon Sep 17 00:00:00 2001 From: Paul Campbell Date: Mon, 12 Aug 2024 21:25:24 +0100 Subject: [PATCH] feat(tui): (experimental) show repo state, messages and git log --- Cargo.lock | 1004 ++++++++++------- Cargo.toml | 6 +- crates/cli/Cargo.toml | 9 +- crates/cli/src/init.rs | 2 +- crates/cli/src/main.rs | 4 +- crates/cli/src/repo/branch.rs | 6 +- crates/cli/src/repo/handlers/advance_main.rs | 25 +- crates/cli/src/repo/handlers/advance_next.rs | 25 +- .../cli/src/repo/handlers/check_ci_status.rs | 13 +- crates/cli/src/repo/handlers/clone_repo.rs | 14 +- .../repo/handlers/load_config_from_repo.rs | 12 +- .../src/repo/handlers/receive_ci_status.rs | 19 +- .../src/repo/handlers/receive_repo_config.rs | 16 +- .../cli/src/repo/handlers/register_webhook.rs | 19 +- .../src/repo/handlers/unregister_webhook.rs | 6 +- crates/cli/src/repo/handlers/validate_repo.rs | 52 +- .../src/repo/handlers/webhook_notification.rs | 25 +- .../src/repo/handlers/webhook_registered.rs | 12 +- crates/cli/src/repo/mod.rs | 69 +- .../cli/src/repo/tests/branch/advance_next.rs | 32 +- crates/cli/src/repo/tests/given.rs | 1 + crates/cli/src/server/actor/handlers/mod.rs | 1 + .../handlers/receive_valid_app_config.rs | 19 +- .../server/actor/handlers/server_update.rs | 14 + crates/cli/src/server/actor/messages.rs | 61 +- crates/cli/src/server/actor/mod.rs | 18 +- crates/cli/src/server/mod.rs | 18 +- crates/cli/src/tui/README.md | 121 ++ .../src/tui/actor/handlers/server_update.rs | 92 +- crates/cli/src/tui/actor/handlers/tick.rs | 50 +- crates/cli/src/tui/actor/messages.rs | 2 +- crates/cli/src/tui/actor/mod.rs | 95 +- crates/cli/src/tui/actor/model.rs | 380 +++++++ .../cli/src/tui/components/configured_app.rs | 52 + .../cli/src/tui/components/forge/collapsed.rs | 15 + .../cli/src/tui/components/forge/expanded.rs | 40 + crates/cli/src/tui/components/forge/mod.rs | 34 + crates/cli/src/tui/components/history.rs | 25 + crates/cli/src/tui/components/mod.rs | 8 + .../cli/src/tui/components/repo/configured.rs | 43 + .../cli/src/tui/components/repo/identified.rs | 34 + .../cli/src/tui/components/repo/identity.rs | 61 + .../cli/src/tui/components/repo/messages.rs | 26 + crates/cli/src/tui/components/repo/mod.rs | 86 ++ crates/cli/src/tui/components/repo/ready.rs | 49 + crates/cli/src/tui/logging.rs | 86 ++ crates/cli/src/tui/mod.rs | 2 + crates/core/src/config/server_repo_config.rs | 3 +- crates/core/src/config/webhook/push.rs | 4 +- crates/core/src/git/commit.rs | 2 +- crates/core/src/git/graph.rs | 1 - crates/core/src/git/push.rs | 2 +- crates/core/src/git/repository/open/mod.rs | 3 +- crates/core/src/git/user_notification.rs | 17 + crates/core/src/git/validation/positions.rs | 13 +- crates/core/src/git/validation/tests.rs | 70 +- 56 files changed, 2280 insertions(+), 638 deletions(-) create mode 100644 crates/cli/src/server/actor/handlers/server_update.rs create mode 100644 crates/cli/src/tui/README.md create mode 100644 crates/cli/src/tui/actor/model.rs create mode 100644 crates/cli/src/tui/components/configured_app.rs create mode 100644 crates/cli/src/tui/components/forge/collapsed.rs create mode 100644 crates/cli/src/tui/components/forge/expanded.rs create mode 100644 crates/cli/src/tui/components/forge/mod.rs create mode 100644 crates/cli/src/tui/components/history.rs create mode 100644 crates/cli/src/tui/components/mod.rs create mode 100644 crates/cli/src/tui/components/repo/configured.rs create mode 100644 crates/cli/src/tui/components/repo/identified.rs create mode 100644 crates/cli/src/tui/components/repo/identity.rs create mode 100644 crates/cli/src/tui/components/repo/messages.rs create mode 100644 crates/cli/src/tui/components/repo/mod.rs create mode 100644 crates/cli/src/tui/components/repo/ready.rs create mode 100644 crates/cli/src/tui/logging.rs diff --git a/Cargo.lock b/Cargo.lock index 4b44be4..f1bba8c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,14 +4,14 @@ version = 3 [[package]] name = "actix" -version = "0.13.3" +version = "0.13.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb72882332b6d6282f428b77ba0358cb2687e61a6f6df6a6d3871e8a177c2d4f" +checksum = "de7fa236829ba0841304542f7614c42b80fca007455315c45c785ccfa873a85b" dependencies = [ "actix-macros", "actix-rt", "actix_derive", - "bitflags 2.5.0", + "bitflags 2.6.0", "bytes", "crossbeam-channel", "futures-core", @@ -34,14 +34,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e01ed3140b2f8d422c68afa1ed2e85d996ea619c988ac834d255db32138655cb" dependencies = [ "quote", - "syn 2.0.60", + "syn 2.0.76", ] [[package]] name = "actix-rt" -version = "2.9.0" +version = "2.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28f32d40287d3f402ae0028a9d54bef51af15c8769492826a69d28f81893151d" +checksum = "24eda4e2a6e042aa4e55ac438a2ae052d3b5da0ecf83d7411e1a368946925208" dependencies = [ "actix-macros", "futures-core", @@ -56,7 +56,7 @@ checksum = "7c7db3d5a9718568e4cf4a537cfd7070e6e6ff7481510d0237fb529ac850f6d3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.76", ] [[package]] @@ -74,6 +74,12 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" +[[package]] +name = "adler2" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" + [[package]] name = "ahash" version = "0.8.11" @@ -81,7 +87,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" dependencies = [ "cfg-if", - "getrandom 0.2.14", + "getrandom 0.2.15", "once_cell", "version_check", "zerocopy", @@ -119,47 +125,48 @@ dependencies = [ [[package]] name = "anstream" -version = "0.6.13" +version = "0.6.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d96bd03f33fe50a863e394ee9718a706f988b9079b20c3784fb726e7678b62fb" +checksum = "64e15c1ab1f89faffbf04a634d5e1962e9074f2741eef6d97f3c4e322426d526" dependencies = [ "anstyle", "anstyle-parse", "anstyle-query", "anstyle-wincon", "colorchoice", + "is_terminal_polyfill", "utf8parse", ] [[package]] name = "anstyle" -version = "1.0.6" +version = "1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8901269c6307e8d93993578286ac0edf7f195079ffff5ebdeea6a59ffb7e36bc" +checksum = "1bec1de6f59aedf83baf9ff929c98f2ad654b97c9510f4e70cf6f661d49fd5b1" [[package]] name = "anstyle-parse" -version = "0.2.3" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c75ac65da39e5fe5ab759307499ddad880d724eed2f6ce5b5e8a26f4f387928c" +checksum = "eb47de1e80c2b463c735db5b217a0ddc39d612e7ac9e2e96a5aed1f57616c1cb" dependencies = [ "utf8parse", ] [[package]] name = "anstyle-query" -version = "1.0.2" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e28923312444cdd728e4738b3f9c9cac739500909bb3d3c94b43551b16517648" +checksum = "6d36fc52c7f6c869915e99412912f22093507da8d9e942ceaf66fe4b7c14422a" dependencies = [ "windows-sys 0.52.0", ] [[package]] name = "anstyle-wincon" -version = "3.0.2" +version = "3.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1cd54b81ec8d6180e24654d0b371ad22fc3dd083b6ff8ba325b72e00c87660a7" +checksum = "5bf74e1b6e971609db8ca7a9ce79fd5768ab6ae46441c572e46cf596f59e57f8" dependencies = [ "anstyle", "windows-sys 0.52.0", @@ -191,9 +198,9 @@ checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b" [[package]] name = "arrayvec" -version = "0.7.4" +version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" +checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" [[package]] name = "assert2" @@ -221,20 +228,26 @@ dependencies = [ [[package]] name = "async-trait" -version = "0.1.80" +version = "0.1.81" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6fa2087f2753a7da8cc1c0dbfcf89579dd57458e36769de5ac750b4671737ca" +checksum = "6e0c28dcc82d7c8ead5cb13beb15405b57b8546e93215673ff8ca0349a028107" dependencies = [ "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.76", ] [[package]] -name = "autocfg" -version = "1.2.0" +name = "atomic-waker" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1fdabc7756949593fe60f30ec81974b613357de856987752631dea1e3394c80" +checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" + +[[package]] +name = "autocfg" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" [[package]] name = "backtrace" @@ -246,7 +259,7 @@ dependencies = [ "cc", "cfg-if", "libc", - "miniz_oxide", + "miniz_oxide 0.7.4", "object", "rustc-demangle", ] @@ -277,9 +290,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.5.0" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" +checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" [[package]] name = "blake2b_simd" @@ -309,12 +322,12 @@ dependencies = [ [[package]] name = "bstr" -version = "1.9.1" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05efc5cfd9110c8416e471df0e96702d58690178e206e61b7173706673c93706" +checksum = "40723b8fb387abc38f4f4a37c09073622e41dd12327033091ef8950659e6dc0c" dependencies = [ "memchr", - "regex-automata 0.4.6", + "regex-automata 0.4.7", "serde", ] @@ -332,9 +345,9 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" -version = "1.6.0" +version = "1.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "514de17de45fdb8dc022b1a7975556c53c86f9f0aa5f534b98977b171857c2c9" +checksum = "8318a53db07bb3f8dca91a600466bdb3f2eaadeedfdbcf02e1accbad9271ba50" [[package]] name = "bytesize" @@ -359,9 +372,12 @@ dependencies = [ [[package]] name = "cc" -version = "1.0.95" +version = "1.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d32a725bc159af97c3e629873bb9f88fb8cf8a4867175f76dc987815ea07c83b" +checksum = "50d2eb3cd3d1bf4529e31c215ee6f93ec5a3d536d9f578f93d9d33ee19562932" +dependencies = [ + "shlex", +] [[package]] name = "cfg-if" @@ -380,7 +396,7 @@ dependencies = [ "js-sys", "num-traits", "wasm-bindgen", - "windows-targets 0.52.5", + "windows-targets 0.52.6", ] [[package]] @@ -395,9 +411,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.4" +version = "4.5.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90bc066a67923782aa8515dbaea16946c5bcc5addbd668bb80af688e53e548a0" +checksum = "ed6719fffa43d0d87e5fd8caeab59be1554fb028cd30edc88fc4369b17971019" dependencies = [ "clap_builder", "clap_derive", @@ -405,9 +421,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.2" +version = "4.5.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae129e2e766ae0ec03484e609954119f123cc1fe650337e155d03b022f24f7b4" +checksum = "216aec2b177652e3846684cbfe25c9964d18ec45234f0f5da5157b207ed1aab6" dependencies = [ "anstream", "anstyle", @@ -417,27 +433,27 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.5.4" +version = "4.5.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "528131438037fd55894f62d6e9f068b8f45ac57ffa77517819645d10aed04f64" +checksum = "501d359d5f3dcaf6ecdeee48833ae73ec6e42723a1e52419c79abf9507eec0a0" dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.76", ] [[package]] name = "clap_lex" -version = "0.7.0" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "98cc8fbded0c607b7ba9dd60cd98df59af97e84d24e49c8557331cfc26d301ce" +checksum = "1462739cb27611015575c0c11df5df7601141071f07518d56fcc1be504cbec97" [[package]] name = "clru" -version = "0.6.1" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8191fa7302e03607ff0e237d4246cc043ff5b3cb9409d995172ba3bea16b807" +checksum = "cbd0f76e066e64fdc5631e3bb46381254deab9ef1158292f27c8c57e3bf3fe59" [[package]] name = "color-eyre" @@ -468,9 +484,9 @@ dependencies = [ [[package]] name = "colorchoice" -version = "1.0.0" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" +checksum = "d3fd119d74b830634cea2a0f58bbd0d54540518a14397557951e79340abc28c0" [[package]] name = "compact_str" @@ -504,42 +520,42 @@ dependencies = [ [[package]] name = "core-foundation-sys" -version = "0.8.6" +version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" [[package]] name = "cpufeatures" -version = "0.2.12" +version = "0.2.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504" +checksum = "51e852e6dc9a5bed1fae92dd2375037bf2b768725bf3be87811edee3249d09ad" dependencies = [ "libc", ] [[package]] name = "crc32fast" -version = "1.4.0" +version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3855a8a784b474f333699ef2bbca9db2c4a1f6d9088a90a2d25b1eb53111eaa" +checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3" dependencies = [ "cfg-if", ] [[package]] name = "crossbeam-channel" -version = "0.5.12" +version = "0.5.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab3db02a9c5b5121e1e42fbdb1aeb65f5e02624cc58c43f2884c6ccac0b82f95" +checksum = "33480d6946193aa8033910124896ca395333cae7e2d1113d1fef6c3272217df2" dependencies = [ "crossbeam-utils", ] [[package]] name = "crossbeam-utils" -version = "0.8.19" +version = "0.8.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345" +checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" [[package]] name = "crossterm" @@ -547,9 +563,9 @@ version = "0.28.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "829d955a0bb380ef178a640b91779e3987da38c9aea133b20614cfed8cdea9c6" dependencies = [ - "bitflags 2.5.0", + "bitflags 2.6.0", "crossterm_winapi", - "mio 1.0.1", + "mio 1.0.2", "parking_lot", "rustix", "signal-hook", @@ -578,11 +594,12 @@ dependencies = [ [[package]] name = "dashmap" -version = "5.5.3" +version = "6.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "978747c1d849a7d2ee5e8adc0159961c48fb7e5db2f06af6723b80123bb53856" +checksum = "804c8821570c3f8b70230c2ba75ffa5c0f9a4189b9a432b6656c536712acae28" dependencies = [ "cfg-if", + "crossbeam-utils", "hashbrown", "lock_api", "once_cell", @@ -622,27 +639,27 @@ checksum = "841ea25b31404c50f2ddc92e028984a42d0fc818c10afee0b1fbda27c995f028" dependencies = [ "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.76", ] [[package]] name = "derive_more" -version = "1.0.0-beta.7" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3249c0372e72f5f93b5c0ca54c0ab76bbf6216b6f718925476fd9bc4ffabb4fe" +checksum = "4a9b99b9cbbe49445b21764dc0625032a89b145a2642e67603e1c936f5458d05" dependencies = [ "derive_more-impl", ] [[package]] name = "derive_more-impl" -version = "1.0.0-beta.7" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "27d919ced7590fc17b5d5a3c63b662e8a7d2324212c4e4dbbed975cafd22d16d" +checksum = "cb7330aeadfbe296029522e6c40f315320aba36fc43a5b3632f3795348f3bd22" dependencies = [ "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.76", "unicode-xid", ] @@ -663,6 +680,15 @@ dependencies = [ "subtle", ] +[[package]] +name = "directories" +version = "5.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a49173b84e034382284f27f1af4dcbbd231ffa358c0fe316541a7337f376a35" +dependencies = [ + "dirs-sys", +] + [[package]] name = "dirs" version = "1.0.5" @@ -670,10 +696,22 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3fd78930633bd1c6e35c4b42b1df7b0cbc6bc191146e512bb3bedf243fcc3901" dependencies = [ "libc", - "redox_users", + "redox_users 0.3.5", "winapi", ] +[[package]] +name = "dirs-sys" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "520f05a5cbd335fae5a99ff7a6ab8627577660ee5cfd6a94a6a929b52ff0321c" +dependencies = [ + "libc", + "option-ext", + "redox_users 0.4.6", + "windows-sys 0.48.0", +] + [[package]] name = "doc-comment" version = "0.3.3" @@ -688,15 +726,15 @@ checksum = "1435fa1053d8b2fbbe9be7e97eca7f33d37b28409959813daefc1446a14247f1" [[package]] name = "dunce" -version = "1.0.4" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56ce8c6da7551ec6c462cbaf3bfbc75131ebbfa1c944aeaa9dab51ca1c5f0c3b" +checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813" [[package]] name = "either" -version = "1.11.0" +version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a47c1c47d2f5964e29c61246e81db715514cd532db6b5116a25ea3c03d6780a2" +checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" [[package]] name = "email-encoding" @@ -725,18 +763,18 @@ dependencies = [ [[package]] name = "env_filter" -version = "0.1.0" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a009aa4810eb158359dda09d0c87378e4bbb89b5a801f016885a4707ba24f7ea" +checksum = "4f2c92ceda6ceec50f43169f9ee8424fe2db276791afde7b2cd8bc084cb376ab" dependencies = [ "log", ] [[package]] name = "env_logger" -version = "0.11.3" +version = "0.11.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38b35839ba51819680ba087cd351788c9a3c476841207e0b8cee0b04722343b9" +checksum = "e13fa619b91fb2381732789fc5de83b45675e882f66623b7d8cb4f643017018d" dependencies = [ "anstream", "anstyle", @@ -752,9 +790,9 @@ checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" [[package]] name = "errno" -version = "0.3.8" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245" +checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" dependencies = [ "libc", "windows-sys 0.52.0", @@ -778,30 +816,30 @@ checksum = "a2a2b11eda1d40935b26cf18f6833c526845ae8c41e58d09af6adeb6f0269183" [[package]] name = "fastrand" -version = "2.1.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fc0510504f03c51ada170672ac806f1f105a88aa97a5281117e1ddc3368e51a" +checksum = "e8c02a5121d4ea3eb16a80748c74f5549a5665e4c21333c6098f283870fbdea6" [[package]] name = "filetime" -version = "0.2.23" +version = "0.2.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ee447700ac8aa0b2f2bd7bc4462ad686ba06baa6727ac149a2d6277f0d240fd" +checksum = "bf401df4a4e3872c4fe8151134cf483738e74b67fc934d6532c882b3d24a4550" dependencies = [ "cfg-if", "libc", - "redox_syscall 0.4.1", - "windows-sys 0.52.0", + "libredox", + "windows-sys 0.59.0", ] [[package]] name = "flate2" -version = "1.0.30" +version = "1.0.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f54427cfd1c7829e2a139fcefea601bf088ebca651d2bf53ebc600eac295dae" +checksum = "324a1be68054ef05ad64b861cc9eaf1d623d2d8cb25b4bf2cb9cdd902b4bf253" dependencies = [ "crc32fast", - "miniz_oxide", + "miniz_oxide 0.8.0", ] [[package]] @@ -905,7 +943,7 @@ checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.76", ] [[package]] @@ -967,9 +1005,9 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.14" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94b22e06ecb0110981051723910cbf0b5f5e09a2062dd7663334ee79a9d1286c" +checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" dependencies = [ "cfg-if", "js-sys", @@ -1005,13 +1043,16 @@ dependencies = [ "assert2", "bytes", "clap", + "color-eyre", "derive-with", "derive_more", + "directories", "git-conventional", "git-next-core", "git-next-forge-forgejo", "git-next-forge-github", "kxio", + "lazy_static", "lettre", "mockall", "notifica", @@ -1029,6 +1070,7 @@ dependencies = [ "time", "toml", "tracing", + "tracing-error", "tracing-subscriber", "ulid", "warp", @@ -1289,7 +1331,7 @@ version = "0.14.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "03f76169faa0dec598eac60f83d7fcdd739ec16596eca8fb144c88973dbe6f8c" dependencies = [ - "bitflags 2.5.0", + "bitflags 2.6.0", "bstr", "gix-path", "libc", @@ -1442,7 +1484,7 @@ version = "0.16.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "74908b4bbc0a0a40852737e5d7889f676f081e340d5451a16e5b4c50d592f111" dependencies = [ - "bitflags 2.5.0", + "bitflags 2.6.0", "bstr", "gix-features", "gix-path", @@ -1488,7 +1530,7 @@ version = "0.35.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0cd4203244444017682176e65fd0180be9298e58ed90bd4a8489a357795ed22d" dependencies = [ - "bitflags 2.5.0", + "bitflags 2.6.0", "bstr", "filetime", "fnv", @@ -1539,7 +1581,7 @@ version = "0.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4063bf329a191a9e24b6f948a17ccf6698c0380297f5e169cee4f1d2ab9475b" dependencies = [ - "bitflags 2.5.0", + "bitflags 2.6.0", "gix-commitgraph", "gix-date", "gix-hash", @@ -1652,7 +1694,7 @@ version = "0.7.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5d23bf239532b4414d0e63b8ab3a65481881f7237ed9647bb10c1e3cc54c5ceb" dependencies = [ - "bitflags 2.5.0", + "bitflags 2.6.0", "bstr", "gix-attributes", "gix-config-value", @@ -1775,7 +1817,7 @@ version = "0.10.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fe4d52f30a737bbece5276fab5d3a8b276dc2650df963e293d0673be34e7a5f" dependencies = [ - "bitflags 2.5.0", + "bitflags 2.6.0", "gix-path", "libc", "windows-sys 0.52.0", @@ -1821,9 +1863,9 @@ dependencies = [ [[package]] name = "gix-tempfile" -version = "14.0.0" +version = "14.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3b0e276cd08eb2a22e9f286a4f13a222a01be2defafa8621367515375644b99" +checksum = "046b4927969fa816a150a0cda2e62c80016fe11fb3c3184e4dddf4e542f108aa" dependencies = [ "dashmap", "gix-fs", @@ -1866,7 +1908,7 @@ version = "0.41.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "030da39af94e4df35472e9318228f36530989327906f38e27807df305fccb780" dependencies = [ - "bitflags 2.5.0", + "bitflags 2.6.0", "gix-commitgraph", "gix-date", "gix-hash", @@ -1996,15 +2038,15 @@ dependencies = [ [[package]] name = "h2" -version = "0.4.4" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "816ec7294445779408f36fe57bc5b7fc1cf59664059096c65f905c1c61f58069" +checksum = "524e8ac6999421f49a846c2d4411f337e53497d8ec55d67753beffa43c5d9205" dependencies = [ + "atomic-waker", "bytes", "fnv", "futures-core", "futures-sink", - "futures-util", "http 1.1.0", "indexmap", "slab", @@ -2065,6 +2107,12 @@ version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" +[[package]] +name = "hermit-abi" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbf6a919d6cf397374f7dfeeea91d974c7c0a7221d0d0f4f20d859d329e53fcc" + [[package]] name = "hex" version = "0.4.3" @@ -2141,9 +2189,9 @@ dependencies = [ [[package]] name = "http-body" -version = "1.0.0" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1cac85db508abc24a2e48553ba12a996e87244a0395ce011e62b37158745d643" +checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" dependencies = [ "bytes", "http 1.1.0", @@ -2151,22 +2199,22 @@ dependencies = [ [[package]] name = "http-body-util" -version = "0.1.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0475f8b2ac86659c21b64320d5d653f9efe42acd2a4e560073ec61a155a34f1d" +checksum = "793429d76616a256bcb62c2a2ec2bed781c8307e797e2598c50010f2bee2544f" dependencies = [ "bytes", - "futures-core", + "futures-util", "http 1.1.0", - "http-body 1.0.0", + "http-body 1.0.1", "pin-project-lite", ] [[package]] name = "httparse" -version = "1.8.0" +version = "1.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" +checksum = "0fcc0b4a115bf80b728eb8ea024ad5bd707b615bfed49e0665b6e0f86fd082d9" [[package]] name = "httpdate" @@ -2182,9 +2230,9 @@ checksum = "5c3b1f728c459d27b12448862017b96ad4767b1ec2ec5e6434e99f1577f085b8" [[package]] name = "hyper" -version = "0.14.28" +version = "0.14.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf96e135eb83a2a8ddf766e426a841d8ddd7449d5f00d34ea02b41d2f19eef80" +checksum = "a152ddd61dfaec7273fe8419ab357f33aee0d914c5f4efbf0d96fa749eea5ec9" dependencies = [ "bytes", "futures-channel", @@ -2206,16 +2254,16 @@ dependencies = [ [[package]] name = "hyper" -version = "1.3.1" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe575dd17d0862a9a33781c8c4696a55c320909004a67a00fb286ba8b1bc496d" +checksum = "50dfd22e0e76d0f662d429a5f80fcaf3855009297eab6a0a9f8543834744ba05" dependencies = [ "bytes", "futures-channel", "futures-util", - "h2 0.4.4", + "h2 0.4.6", "http 1.1.0", - "http-body 1.0.0", + "http-body 1.0.1", "httparse", "itoa", "pin-project-lite", @@ -2226,19 +2274,20 @@ dependencies = [ [[package]] name = "hyper-rustls" -version = "0.26.0" +version = "0.27.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0bea761b46ae2b24eb4aef630d8d1c398157b6fc29e6350ecf090a0b70c952c" +checksum = "5ee4be2c948921a1a5320b629c4193916ed787a7f7f293fd3f7f5a6c9de74155" dependencies = [ "futures-util", "http 1.1.0", - "hyper 1.3.1", + "hyper 1.4.1", "hyper-util", "rustls", "rustls-pki-types", "tokio", "tokio-rustls", "tower-service", + "webpki-roots", ] [[package]] @@ -2249,7 +2298,7 @@ checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0" dependencies = [ "bytes", "http-body-util", - "hyper 1.3.1", + "hyper 1.4.1", "hyper-util", "native-tls", "tokio", @@ -2259,16 +2308,16 @@ dependencies = [ [[package]] name = "hyper-util" -version = "0.1.3" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca38ef113da30126bbff9cd1705f9273e15d45498615d138b0c20279ac7a76aa" +checksum = "cde7055719c54e36e95e8719f95883f22072a48ede39db7fc17a4e1d5281e9b9" dependencies = [ "bytes", "futures-channel", "futures-util", "http 1.1.0", - "http-body 1.0.0", - "hyper 1.3.1", + "http-body 1.0.1", + "hyper 1.4.1", "pin-project-lite", "socket2", "tokio", @@ -2328,9 +2377,9 @@ checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683" [[package]] name = "indexmap" -version = "2.2.6" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26" +checksum = "93ead53efc7ea8ed3cfb0c79fc8023fbb782a5432b52830b6518941cebe6505c" dependencies = [ "equivalent", "hashbrown", @@ -2363,7 +2412,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b23a0c8dfe501baac4adf6ebbfa6eddf8f0c07f56b058cc1288017e32397846c" dependencies = [ "quote", - "syn 2.0.60", + "syn 2.0.76", ] [[package]] @@ -2384,15 +2433,21 @@ checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3" [[package]] name = "is-terminal" -version = "0.4.12" +version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f23ff5ef2b80d608d61efee834934d862cd92461afc0560dedf493e4c033738b" +checksum = "261f68e344040fbd0edea105bef17c66edf46f984ddb1115b775ce31be948f4b" dependencies = [ - "hermit-abi", + "hermit-abi 0.4.0", "libc", "windows-sys 0.52.0", ] +[[package]] +name = "is_terminal_polyfill" +version = "1.70.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" + [[package]] name = "itertools" version = "0.13.0" @@ -2410,12 +2465,12 @@ checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" [[package]] name = "jiff" -version = "0.1.8" +version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db2b7379a75544c94b3da32821b0bf41f9062e9970e23b78cc577d0d89676d16" +checksum = "8ef8bc400f8312944a9f879db116fed372c4f0859af672eba2a80f79c767dd19" dependencies = [ "jiff-tzdb-platform", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -2435,9 +2490,9 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.69" +version = "0.3.70" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d" +checksum = "1868808506b929d7b0cfa8f75951347aa71bb21144b7791bae35d9bccfcfe37a" dependencies = [ "wasm-bindgen", ] @@ -2464,9 +2519,9 @@ dependencies = [ [[package]] name = "kstring" -version = "2.0.0" +version = "2.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec3066350882a1cd6d950d055997f379ac37fd39f81cd4d8ed186032eb3c5747" +checksum = "558bf9508a558512042d3095138b1f7b8fe90c5467d94f9f1da28b3731c5dbd1" dependencies = [ "static_assertions", ] @@ -2493,9 +2548,9 @@ dependencies = [ [[package]] name = "lazy_static" -version = "1.4.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] name = "lettre" @@ -2524,9 +2579,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.154" +version = "0.2.158" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae743338b92ff9146ce83992f766a31066a91a8c84a45e0e9f21e7cf6de6d346" +checksum = "d8adc4bb1803a324070e64a98ae98f38934d91957a99cfb3a43dcbc01bc56439" [[package]] name = "libdbus-sys" @@ -2538,10 +2593,21 @@ dependencies = [ ] [[package]] -name = "linux-raw-sys" -version = "0.4.13" +name = "libredox" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c" +checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" +dependencies = [ + "bitflags 2.6.0", + "libc", + "redox_syscall 0.5.3", +] + +[[package]] +name = "linux-raw-sys" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" [[package]] name = "lock_api" @@ -2555,9 +2621,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.21" +version = "0.4.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" +checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" [[package]] name = "lru" @@ -2606,14 +2672,14 @@ checksum = "5cf92c10c7e361d6b99666ec1c6f9805b0bea2c3bd8c78dc6fe98ac5bd78db11" dependencies = [ "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.76", ] [[package]] name = "memchr" -version = "2.7.2" +version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" [[package]] name = "memmap2" @@ -2632,9 +2698,9 @@ checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" [[package]] name = "mime_guess" -version = "2.0.4" +version = "2.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4192263c238a5f0d0c6bfd21f336a313a4ce1c450542449ca191bb657b4642ef" +checksum = "f7c44f8e672c00fe5308fa235f821cb4198414e1c77935c1ab6948d3fd78550e" dependencies = [ "mime", "unicase", @@ -2648,13 +2714,22 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" [[package]] name = "miniz_oxide" -version = "0.7.2" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d811f3e15f28568be3407c8e7fdb6514c1cda3cb30683f15b6a1a1dc4ea14a7" +checksum = "b8a240ddb74feaf34a79a7add65a741f3167852fba007066dcac1ca548d89c08" dependencies = [ "adler", ] +[[package]] +name = "miniz_oxide" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2d80299ef12ff69b16a84bb182e3b9df68b5a91574d3d4fa6e41b65deec4df1" +dependencies = [ + "adler2", +] + [[package]] name = "mio" version = "0.8.11" @@ -2669,11 +2744,11 @@ dependencies = [ [[package]] name = "mio" -version = "1.0.1" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4569e456d394deccd22ce1c1913e6ea0e54519f577285001215d33557431afe4" +checksum = "80e04d1dcff3aae0704555fe5fee3bcfaf3d1fdf8a7e521d5b9d2b42acb52cec" dependencies = [ - "hermit-abi", + "hermit-abi 0.3.9", "libc", "log", "wasi 0.11.0+wasi-snapshot-preview1", @@ -2703,7 +2778,7 @@ dependencies = [ "cfg-if", "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.76", ] [[package]] @@ -2726,11 +2801,10 @@ dependencies = [ [[package]] name = "native-tls" -version = "0.2.11" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07226173c32f2926027b63cce4bcd8076c3552846cbe7925f3aaffeac0a3b92e" +checksum = "a8614eb2c83d59d1c8cc974dd3f920198647674a0a035e1af1fa58707e317466" dependencies = [ - "lazy_static", "libc", "log", "openssl", @@ -2769,7 +2843,7 @@ version = "6.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6205bd8bb1e454ad2e27422015fb5e4f2bcc7e08fa8f27058670d208324a4d2d" dependencies = [ - "bitflags 2.5.0", + "bitflags 2.6.0", "crossbeam-channel", "filetime", "fsevent-sys", @@ -2817,16 +2891,6 @@ dependencies = [ "autocfg", ] -[[package]] -name = "num_cpus" -version = "1.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" -dependencies = [ - "hermit-abi", - "libc", -] - [[package]] name = "objc" version = "0.2.7" @@ -2873,11 +2937,11 @@ checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" [[package]] name = "openssl" -version = "0.10.64" +version = "0.10.66" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95a0481286a310808298130d22dd1fef0fa571e05a8f44ec801801e84b216b1f" +checksum = "9529f4786b70a3e8c61e11179af17ab6188ad8d0ded78c5529441ed39d4bd9c1" dependencies = [ - "bitflags 2.5.0", + "bitflags 2.6.0", "cfg-if", "foreign-types", "libc", @@ -2894,7 +2958,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.76", ] [[package]] @@ -2905,9 +2969,9 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" [[package]] name = "openssl-sys" -version = "0.9.102" +version = "0.9.103" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c597637d56fbc83893a35eb0dd04b2b8e7a50c91e64e9493e398b5df4fb45fa2" +checksum = "7f9e8deee91df40a943c71b917e5874b951d32a802526c85721ce3b776c929d6" dependencies = [ "cc", "libc", @@ -2915,6 +2979,12 @@ dependencies = [ "vcpkg", ] +[[package]] +name = "option-ext" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" + [[package]] name = "overload" version = "0.1.1" @@ -2929,9 +2999,9 @@ checksum = "c1b04fb49957986fdce4d6ee7a65027d55d4b6d2265e5848bbb507b58ccfdb6f" [[package]] name = "parking_lot" -version = "0.12.2" +version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e4af0ca4f6caed20e900d564c242b8e5d4903fdacf31d3daf527b66fe6f42fb" +checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" dependencies = [ "lock_api", "parking_lot_core", @@ -2945,9 +3015,9 @@ checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" dependencies = [ "cfg-if", "libc", - "redox_syscall 0.5.1", + "redox_syscall 0.5.3", "smallvec", - "windows-targets 0.52.5", + "windows-targets 0.52.6", ] [[package]] @@ -2991,7 +3061,7 @@ checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" dependencies = [ "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.76", ] [[package]] @@ -3026,15 +3096,18 @@ checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" [[package]] name = "ppv-lite86" -version = "0.2.17" +version = "0.2.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" +checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" +dependencies = [ + "zerocopy", +] [[package]] name = "predicates" -version = "3.1.0" +version = "3.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68b87bfd4605926cdfefc1c3b5f8fe560e3feca9d5552cf68c466d3d8236c7e8" +checksum = "7e9086cc7640c29a356d1a29fd134380bee9d8f79a17410aa76e7ad295f42c97" dependencies = [ "anstyle", "predicates-core", @@ -3042,15 +3115,15 @@ dependencies = [ [[package]] name = "predicates-core" -version = "1.0.6" +version = "1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b794032607612e7abeb4db69adb4e33590fa6cf1149e95fd7cb00e634b92f174" +checksum = "ae8177bee8e75d6846599c6b9ff679ed51e882816914eec639944d7c9aa11931" [[package]] name = "predicates-tree" -version = "1.0.9" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "368ba315fb8c5052ab692e68a0eefec6ec57b23a36959c14496f0b0df2c0cecf" +checksum = "41b740d195ed3166cd147c8047ec98db0e22ec019eb8eeb76d343b795304fb13" dependencies = [ "predicates-core", "termtree", @@ -3077,9 +3150,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.81" +version = "1.0.86" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d1597b0c024618f09a9c3b8655b7e430397a36d23fdafec26d6965e9eec3eba" +checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" dependencies = [ "unicode-ident", ] @@ -3104,10 +3177,58 @@ dependencies = [ ] [[package]] -name = "quote" -version = "1.0.36" +name = "quinn" +version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" +checksum = "b22d8e7369034b9a7132bc2008cac12f2013c8132b45e0554e6e20e2617f2156" +dependencies = [ + "bytes", + "pin-project-lite", + "quinn-proto", + "quinn-udp", + "rustc-hash", + "rustls", + "socket2", + "thiserror", + "tokio", + "tracing", +] + +[[package]] +name = "quinn-proto" +version = "0.11.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba92fb39ec7ad06ca2582c0ca834dfeadcaf06ddfc8e635c80aa7e1c05315fdd" +dependencies = [ + "bytes", + "rand", + "ring", + "rustc-hash", + "rustls", + "slab", + "thiserror", + "tinyvec", + "tracing", +] + +[[package]] +name = "quinn-udp" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8bffec3605b73c6f1754535084a85229fa8a30f86014e6c81aeec4abb68b0285" +dependencies = [ + "libc", + "once_cell", + "socket2", + "tracing", + "windows-sys 0.52.0", +] + +[[package]] +name = "quote" +version = "1.0.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" dependencies = [ "proc-macro2", ] @@ -3145,16 +3266,16 @@ version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ - "getrandom 0.2.14", + "getrandom 0.2.15", ] [[package]] name = "ratatui" -version = "0.28.0" +version = "0.28.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ba6a365afbe5615999275bea2446b970b10a41102500e27ce7678d50d978303" +checksum = "fdef7f9be5c0122f890d58bdf4d964349ba6a6161f705907526d891efabba57d" dependencies = [ - "bitflags 2.5.0", + "bitflags 2.6.0", "cassowary", "compact_str", "crossterm", @@ -3177,20 +3298,11 @@ checksum = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce" [[package]] name = "redox_syscall" -version = "0.4.1" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" +checksum = "2a908a6e00f1fdd0dfd9c0eb08ce85126f6d8bbda50017e74bc4a4b7d4a926a4" dependencies = [ - "bitflags 1.3.2", -] - -[[package]] -name = "redox_syscall" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "469052894dcb553421e483e4209ee581a45100d31b4018de03e5a7ad86374a7e" -dependencies = [ - "bitflags 2.5.0", + "bitflags 2.6.0", ] [[package]] @@ -3205,15 +3317,26 @@ dependencies = [ ] [[package]] -name = "regex" -version = "1.10.4" +name = "redox_users" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c117dbdfde9c8308975b6a18d71f3f385c89461f7b3fb054288ecf2a2058ba4c" +checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43" +dependencies = [ + "getrandom 0.2.15", + "libredox", + "thiserror", +] + +[[package]] +name = "regex" +version = "1.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4219d74c6b67a3654a9fbebc4b419e22126d13d2f3c4a07ee0cb61ff79a79619" dependencies = [ "aho-corasick", "memchr", - "regex-automata 0.4.6", - "regex-syntax 0.8.3", + "regex-automata 0.4.7", + "regex-syntax 0.8.4", ] [[package]] @@ -3227,13 +3350,13 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.6" +version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea" +checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df" dependencies = [ "aho-corasick", "memchr", - "regex-syntax 0.8.3", + "regex-syntax 0.8.4", ] [[package]] @@ -3244,9 +3367,9 @@ checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" [[package]] name = "regex-syntax" -version = "0.8.3" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56" +checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" [[package]] name = "relative-path" @@ -3256,9 +3379,9 @@ checksum = "ba39f3699c378cd8970968dcbff9c43159ea4cfbd88d43c00b22f2ef10a435d2" [[package]] name = "reqwest" -version = "0.12.4" +version = "0.12.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "566cafdd92868e0939d3fb961bd0dc25fcfaaed179291093b3d43e6b3150ea10" +checksum = "f8f4955649ef5c38cc7f9e8aa41761d48fb9677197daea9984dc54f56aad5e63" dependencies = [ "base64 0.22.1", "bytes", @@ -3266,11 +3389,11 @@ dependencies = [ "futures-channel", "futures-core", "futures-util", - "h2 0.4.4", + "h2 0.4.6", "http 1.1.0", - "http-body 1.0.0", + "http-body 1.0.1", "http-body-util", - "hyper 1.3.1", + "hyper 1.4.1", "hyper-rustls", "hyper-tls", "hyper-util", @@ -3282,6 +3405,7 @@ dependencies = [ "once_cell", "percent-encoding", "pin-project-lite", + "quinn", "rustls", "rustls-pemfile", "rustls-pki-types", @@ -3299,7 +3423,7 @@ dependencies = [ "wasm-bindgen-futures", "web-sys", "webpki-roots", - "winreg", + "windows-registry", ] [[package]] @@ -3310,7 +3434,7 @@ checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d" dependencies = [ "cc", "cfg-if", - "getrandom 0.2.14", + "getrandom 0.2.15", "libc", "spin", "untrusted", @@ -3343,7 +3467,7 @@ dependencies = [ "regex", "relative-path", "rustc_version", - "syn 2.0.60", + "syn 2.0.76", "unicode-ident", ] @@ -3361,9 +3485,15 @@ dependencies = [ [[package]] name = "rustc-demangle" -version = "0.1.23" +version = "0.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" +checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" + +[[package]] +name = "rustc-hash" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "583034fd73374156e66797ed8e5b0d5690409c9226b22d87cb7f19821c05d152" [[package]] name = "rustc_version" @@ -3380,7 +3510,7 @@ version = "0.38.34" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f" dependencies = [ - "bitflags 2.5.0", + "bitflags 2.6.0", "errno", "libc", "linux-raw-sys", @@ -3389,11 +3519,11 @@ dependencies = [ [[package]] name = "rustls" -version = "0.22.4" +version = "0.23.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf4ef73721ac7bcd79b2b315da7779d8fc09718c6b3d2d1b2d94850eb8c18432" +checksum = "c58f8c84392efc0a126acce10fa59ff7b3d2ac06ab451a33f2741989b806b044" dependencies = [ - "log", + "once_cell", "ring", "rustls-pki-types", "rustls-webpki", @@ -3403,9 +3533,9 @@ dependencies = [ [[package]] name = "rustls-pemfile" -version = "2.1.2" +version = "2.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29993a25686778eb88d4189742cd713c9bce943bc54251a33509dc63cbacf73d" +checksum = "196fe16b00e106300d3e45ecfcb764fa292a535d7326a29a5875c579c7417425" dependencies = [ "base64 0.22.1", "rustls-pki-types", @@ -3413,15 +3543,15 @@ dependencies = [ [[package]] name = "rustls-pki-types" -version = "1.5.0" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "beb461507cee2c2ff151784c52762cf4d9ff6a61f3e80968600ed24fa837fa54" +checksum = "fc0a2ce646f8655401bb81e7927b812614bd5d91dbc968696be50603510fcaf0" [[package]] name = "rustls-webpki" -version = "0.102.3" +version = "0.102.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3bce581c0dd41bce533ce695a1437fa16a7ab5ac3ccfa99fe1a620a7885eabf" +checksum = "8e6b52d4fda176fd835fdc55a835d4a89b8499cad995885a21149d5ad62f852e" dependencies = [ "ring", "rustls-pki-types", @@ -3436,9 +3566,9 @@ checksum = "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6" [[package]] name = "ryu" -version = "1.0.17" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e86697c916019a8588c99b5fac3cead74ec0b4b819707a682fd4d23fa0ce1ba1" +checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" [[package]] name = "same-file" @@ -3481,11 +3611,11 @@ dependencies = [ [[package]] name = "security-framework" -version = "2.10.0" +version = "2.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "770452e37cad93e0a50d5abc3990d2bc351c36d0328f86cefec2f2fb206eaef6" +checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" dependencies = [ - "bitflags 1.3.2", + "bitflags 2.6.0", "core-foundation", "core-foundation-sys", "libc", @@ -3494,9 +3624,9 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.10.0" +version = "2.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41f3cc463c0ef97e11c3461a9d3787412d30e8e7eb907c79180c4a57bf7c04ef" +checksum = "75da29fe9b9b08fe9d6b22b5b4bcbc75d8db3aa31e639aa56bb62e9d46bfceaf" dependencies = [ "core-foundation-sys", "libc", @@ -3504,9 +3634,9 @@ dependencies = [ [[package]] name = "semver" -version = "1.0.22" +version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92d43fe69e652f3df9bdc2b85b2854a0825b86e4fb76bc44d945137d053639ca" +checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" [[package]] name = "sendmail" @@ -3516,9 +3646,9 @@ checksum = "b534d6dc60c5df66cda0abd5ebaff07e208722bd0188211a03dd5a21812b9e4a" [[package]] name = "serde" -version = "1.0.199" +version = "1.0.209" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c9f6e76df036c77cd94996771fb40db98187f096dd0b9af39c6c6e452ba966a" +checksum = "99fce0ffe7310761ca6bf9faf5115afbc19688edd00171d81b1bb1b116c63e09" dependencies = [ "serde_derive", ] @@ -3537,31 +3667,32 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.199" +version = "1.0.209" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11bd257a6541e141e42ca6d24ae26f7714887b47e89aa739099104c7e4d3b7fc" +checksum = "a5831b979fd7b5439637af1752d535ff49f4860c0f341d1baeb6faf0f4242170" dependencies = [ "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.76", ] [[package]] name = "serde_json" -version = "1.0.116" +version = "1.0.127" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e17db7126d17feb94eb3fad46bf1a96b034e8aacbc2e775fe81505f8b0b2813" +checksum = "8043c06d9f82bd7271361ed64f415fe5e12a77fdb52e573e7f06a516dea329ad" dependencies = [ "itoa", + "memchr", "ryu", "serde", ] [[package]] name = "serde_spanned" -version = "0.6.5" +version = "0.6.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb3622f419d1296904700073ea6cc23ad690adbd66f13ea683df73298736f0c1" +checksum = "eb5b1b31579f3811bf615c144393417496f152e12ac8b7663bf664f4a815306d" dependencies = [ "serde", ] @@ -3591,9 +3722,9 @@ dependencies = [ [[package]] name = "sha1_smol" -version = "1.0.0" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae1a47186c03a32177042e55dbc5fd5aee900b8e0069a8d70fba96a9375cd012" +checksum = "bbfa15b3dddfee50a0fff136974b3e1bde555604ba463834a7eb7deb6417705d" [[package]] name = "sha2" @@ -3621,6 +3752,12 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24188a676b6ae68c3b2cb3a01be17fbf7240ce009799bb56d5b1409051e78fde" +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + [[package]] name = "signal-hook" version = "0.3.17" @@ -3638,7 +3775,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34db1a06d485c9142248b7a054f034b349b212551f3dfd19c94d45a754a217cd" dependencies = [ "libc", - "mio 1.0.1", + "mio 1.0.2", "signal-hook", ] @@ -3684,15 +3821,15 @@ checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" [[package]] name = "stacker" -version = "0.1.15" +version = "0.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c886bd4480155fd3ef527d45e9ac8dd7118a898a46530b7b94c3e21866259fce" +checksum = "95a5daa25ea337c85ed954c0496e3bdd2c7308cc3b24cf7b50d04876654c579f" dependencies = [ "cc", "cfg-if", "libc", "psm", - "winapi", + "windows-sys 0.36.1", ] [[package]] @@ -3758,14 +3895,14 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 2.0.60", + "syn 2.0.76", ] [[package]] name = "subtle" -version = "2.5.0" +version = "2.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" [[package]] name = "syn" @@ -3780,9 +3917,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.60" +version = "2.0.76" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "909518bc7b1c9b779f1bbf07f2929d35af9f0f37e47c6e9ef7f9dddc1e1821f3" +checksum = "578e081a14e0cefc3279b0472138c513f37b41a08d5a3cca9b6e4e8ceb6cd525" dependencies = [ "proc-macro2", "quote", @@ -3791,26 +3928,29 @@ dependencies = [ [[package]] name = "sync_wrapper" -version = "0.1.2" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" +checksum = "a7065abeca94b6a8a577f9bd45aa0867a2238b74e8eb67cf10d492bc39351394" +dependencies = [ + "futures-core", +] [[package]] name = "system-configuration" -version = "0.5.1" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7" +checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b" dependencies = [ - "bitflags 1.3.2", + "bitflags 2.6.0", "core-foundation", "system-configuration-sys", ] [[package]] name = "system-configuration-sys" -version = "0.5.0" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9" +checksum = "8e1d1b10ced5ca923a1fcb8d03e96b8d3268065d724548c0211415ff6ac6bac4" dependencies = [ "core-foundation-sys", "libc", @@ -3824,14 +3964,15 @@ checksum = "8bdb6fa0dfa67b38c1e66b7041ba9dcf23b99d8121907cd31c807a332f7a0bbb" [[package]] name = "tempfile" -version = "3.10.1" +version = "3.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85b77fafb263dd9d05cbeac119526425676db3784113aa9295c88498cbf8bff1" +checksum = "04cbcdd0c794ebb0d4cf35e88edd2f7d2c4c3e9a5a6dab322839b321c6a87a64" dependencies = [ "cfg-if", "fastrand", + "once_cell", "rustix", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -3859,27 +4000,27 @@ checksum = "5999e24eaa32083191ba4e425deb75cdf25efefabe5aaccb7446dd0d4122a3f5" dependencies = [ "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.76", ] [[package]] name = "thiserror" -version = "1.0.59" +version = "1.0.63" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0126ad08bff79f29fc3ae6a55cc72352056dfff61e3ff8bb7129476d44b23aa" +checksum = "c0342370b38b6a11b6cc11d6a805569958d54cfa061a29969c3b5ce2ea405724" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.59" +version = "1.0.63" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d1cd413b5d558b4c5bf3680e324a6fa5014e7b7c067a51e69dbdf47eb7148b66" +checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261" dependencies = [ "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.76", ] [[package]] @@ -3913,9 +4054,9 @@ checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" [[package]] name = "tinyvec" -version = "1.6.0" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" +checksum = "445e881f4f6d382d5f27c034e25eb92edd7c784ceab92a0937db7f2e9471b938" dependencies = [ "tinyvec_macros", ] @@ -3928,32 +4069,31 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.37.0" +version = "1.39.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1adbebffeca75fcfd058afa480fb6c0b81e165a0323f9c9d39c9697e37c46787" +checksum = "9babc99b9923bfa4804bd74722ff02c0381021eafa4db9949217e3be8e84fff5" dependencies = [ "backtrace", "bytes", "libc", - "mio 0.8.11", - "num_cpus", + "mio 1.0.2", "parking_lot", "pin-project-lite", "signal-hook-registry", "socket2", "tokio-macros", - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] name = "tokio-macros" -version = "2.2.0" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" +checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752" dependencies = [ "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.76", ] [[package]] @@ -3968,9 +4108,9 @@ dependencies = [ [[package]] name = "tokio-rustls" -version = "0.25.0" +version = "0.26.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "775e0c0f0adb3a2f22a00c4745d728b479985fc15ee7ca6a2608388c5569860f" +checksum = "0c7bc40d0e5a97695bb96e27995cd3a08538541b0a846f65bba7a359f36700d4" dependencies = [ "rustls", "rustls-pki-types", @@ -3991,35 +4131,34 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.7.10" +version = "0.7.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5419f34732d9eb6ee4c3578b7989078579b7f039cbbb9ca2c4da015749371e15" +checksum = "9cf6b47b3771c49ac75ad09a6162f53ad4b8088b76ac60e8ec1455b31a189fe1" dependencies = [ "bytes", "futures-core", "futures-sink", "pin-project-lite", "tokio", - "tracing", ] [[package]] name = "toml" -version = "0.8.12" +version = "0.8.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e9dd1545e8208b4a5af1aa9bbd0b4cf7e9ea08fabc5d0a5c67fcaafa17433aa3" +checksum = "a1ed1f98e3fdc28d6d910e6737ae6ab1a93bf1985935a1193e68f93eeb68d24e" dependencies = [ "serde", "serde_spanned", "toml_datetime", - "toml_edit 0.22.12", + "toml_edit 0.22.20", ] [[package]] name = "toml_datetime" -version = "0.6.5" +version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3550f4e9685620ac18a50ed434eb3aec30db8ba93b0287467bca5826ea25baf1" +checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" dependencies = [ "serde", ] @@ -4037,9 +4176,9 @@ dependencies = [ [[package]] name = "toml_edit" -version = "0.22.12" +version = "0.22.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3328d4f68a705b2a4498da1d580585d39a6510f98318a2cec3018a7ec61ddef" +checksum = "583c44c02ad26b0c3f3066fe629275e50627026c51ac2e595cca4c230ce1ce1d" dependencies = [ "indexmap", "serde", @@ -4061,20 +4200,19 @@ dependencies = [ "tokio", "tower-layer", "tower-service", - "tracing", ] [[package]] name = "tower-layer" -version = "0.3.2" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c20c8dbed6283a09604c3e69b4b7eeb54e298b8a600d4d5ecb5ad39de609f1d0" +checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" [[package]] name = "tower-service" -version = "0.3.2" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" +checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" [[package]] name = "tracing" @@ -4096,7 +4234,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.76", ] [[package]] @@ -4181,11 +4319,11 @@ checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" [[package]] name = "ulid" -version = "1.1.2" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34778c17965aa2a08913b57e1f34db9b4a63f5de31768b55bf20d2795f921259" +checksum = "04f903f293d11f31c0c29e4148f6dc0d033a7f80cebc0282bea147611667d289" dependencies = [ - "getrandom 0.2.14", + "getrandom 0.2.15", "rand", "web-time", ] @@ -4196,7 +4334,7 @@ version = "3.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7c8a2469e56e6e5095c82ccd3afb98dad95f7af7929aab6d8ba8d6e0f73657da" dependencies = [ - "arrayvec 0.7.4", + "arrayvec 0.7.6", ] [[package]] @@ -4260,9 +4398,9 @@ checksum = "0336d538f7abc86d282a4189614dfaa90810dfc2c6f6427eaf88e16311dd225d" [[package]] name = "unicode-xid" -version = "0.2.4" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" +checksum = "229730647fbc343e3a80e463c1db7f78f3855d3f3739bee0dda773c9a037c90a" [[package]] name = "untrusted" @@ -4289,9 +4427,9 @@ checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" [[package]] name = "utf8parse" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "valuable" @@ -4307,9 +4445,9 @@ checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" [[package]] name = "version_check" -version = "0.9.4" +version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" [[package]] name = "walkdir" @@ -4341,7 +4479,7 @@ dependencies = [ "futures-util", "headers", "http 0.2.12", - "hyper 0.14.28", + "hyper 0.14.30", "log", "mime", "mime_guess", @@ -4373,34 +4511,35 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.92" +version = "0.2.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8" +checksum = "a82edfc16a6c469f5f44dc7b571814045d60404b55a0ee849f9bcfa2e63dd9b5" dependencies = [ "cfg-if", + "once_cell", "wasm-bindgen-macro", ] [[package]] name = "wasm-bindgen-backend" -version = "0.2.92" +version = "0.2.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da" +checksum = "9de396da306523044d3302746f1208fa71d7532227f15e347e2d93e4145dd77b" dependencies = [ "bumpalo", "log", "once_cell", "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.76", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-futures" -version = "0.4.42" +version = "0.4.43" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76bc14366121efc8dbb487ab05bcc9d346b3b5ec0eaa76e46594cabbe51762c0" +checksum = "61e9300f63a621e96ed275155c108eb6f843b6a26d053f122ab69724559dc8ed" dependencies = [ "cfg-if", "js-sys", @@ -4410,9 +4549,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.92" +version = "0.2.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726" +checksum = "585c4c91a46b072c92e908d99cb1dcdf95c5218eeb6f3bf1efa991ee7a68cccf" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -4420,28 +4559,28 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.92" +version = "0.2.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" +checksum = "afc340c74d9005395cf9dd098506f7f44e38f2b4a21c6aaacf9a105ea5e1e836" dependencies = [ "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.76", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.92" +version = "0.2.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" +checksum = "c62a0a307cb4a311d3a07867860911ca130c3494e8c2719593806c08bc5d0484" [[package]] name = "web-sys" -version = "0.3.69" +version = "0.3.70" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77afa9a11836342370f4817622a2f0f418b134426d91a82dfb48f532d2ec13ef" +checksum = "26fdeaafd9bd129f65e7c031593c24d62186301e0c72c8978fa1678be7d532c0" dependencies = [ "js-sys", "wasm-bindgen", @@ -4459,9 +4598,9 @@ dependencies = [ [[package]] name = "webpki-roots" -version = "0.26.1" +version = "0.26.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3de34ae270483955a94f4b21bdaaeb83d508bb84a01435f393818edb0012009" +checksum = "bd7c23921eeb1713a4e851530e9b9756e4fb0e89978582942612524cf09f01cd" dependencies = [ "rustls-pki-types", ] @@ -4484,11 +4623,11 @@ checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] name = "winapi-util" -version = "0.1.8" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d4cc384e1e73b93bafa6fb4f1df8c41695c8a91cf9c4c64358067d15a7b6c6b" +checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" dependencies = [ - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -4504,7 +4643,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e48a53791691ab099e5e2ad123536d0fff50652600abaf43bbf952894110d0be" dependencies = [ "windows-core", - "windows-targets 0.52.5", + "windows-targets 0.52.6", ] [[package]] @@ -4513,7 +4652,50 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" dependencies = [ - "windows-targets 0.52.5", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-registry" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e400001bb720a623c1c69032f8e3e4cf09984deec740f007dd2b03ec864804b0" +dependencies = [ + "windows-result", + "windows-strings", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-result" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d1043d8214f791817bab27572aaa8af63732e11bf84aa21a45a78d6c317ae0e" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-strings" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cd9b125c486025df0eabcb585e62173c6c9eddcec5d117d3b6e8c30e2ee4d10" +dependencies = [ + "windows-result", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea04155a16a59f9eab786fe12a4a450e75cdb175f9e0d80da1e17db09f55b8d2" +dependencies = [ + "windows_aarch64_msvc 0.36.1", + "windows_i686_gnu 0.36.1", + "windows_i686_msvc 0.36.1", + "windows_x86_64_gnu 0.36.1", + "windows_x86_64_msvc 0.36.1", ] [[package]] @@ -4531,7 +4713,16 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets 0.52.5", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets 0.52.6", ] [[package]] @@ -4551,18 +4742,18 @@ dependencies = [ [[package]] name = "windows-targets" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" dependencies = [ - "windows_aarch64_gnullvm 0.52.5", - "windows_aarch64_msvc 0.52.5", - "windows_i686_gnu 0.52.5", + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", "windows_i686_gnullvm", - "windows_i686_msvc 0.52.5", - "windows_x86_64_gnu 0.52.5", - "windows_x86_64_gnullvm 0.52.5", - "windows_x86_64_msvc 0.52.5", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", ] [[package]] @@ -4573,9 +4764,15 @@ checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" [[package]] name = "windows_aarch64_gnullvm" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9bb8c3fd39ade2d67e9874ac4f3db21f0d710bee00fe7cab16949ec184eeaa47" [[package]] name = "windows_aarch64_msvc" @@ -4585,9 +4782,15 @@ checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" [[package]] name = "windows_aarch64_msvc" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[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_gnu" @@ -4597,15 +4800,21 @@ checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" [[package]] name = "windows_i686_gnu" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" [[package]] name = "windows_i686_gnullvm" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_msvc" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024" [[package]] name = "windows_i686_msvc" @@ -4615,9 +4824,15 @@ checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" [[package]] name = "windows_i686_msvc" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[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_gnu" @@ -4627,9 +4842,9 @@ checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" [[package]] name = "windows_x86_64_gnu" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" [[package]] name = "windows_x86_64_gnullvm" @@ -4639,9 +4854,15 @@ checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" [[package]] name = "windows_x86_64_gnullvm" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680" [[package]] name = "windows_x86_64_msvc" @@ -4651,9 +4872,9 @@ checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" [[package]] name = "windows_x86_64_msvc" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "winnow" @@ -4673,16 +4894,6 @@ dependencies = [ "memchr", ] -[[package]] -name = "winreg" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a277a57398d4bfa075df44f501a17cfdf8542d224f0d36095a2adc7aee4ef0a5" -dependencies = [ - "cfg-if", - "windows-sys 0.48.0", -] - [[package]] name = "winrt" version = "0.6.0" @@ -4694,9 +4905,9 @@ dependencies = [ [[package]] name = "xml-rs" -version = "0.8.20" +version = "0.8.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "791978798f0597cfc70478424c2b4fdc2b7a8024aaff78497ef00f24ef674193" +checksum = "539a77ee7c0de333dcc6da69b177380a0b81e0dacfa4f7344c465a36871ee601" [[package]] name = "yansi" @@ -4706,26 +4917,27 @@ checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec" [[package]] name = "zerocopy" -version = "0.7.32" +version = "0.7.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74d4d3961e53fa4c9a25a8637fc2bfaf2595b3d3ae34875568a5cf64787716be" +checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" dependencies = [ + "byteorder", "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.7.32" +version = "0.7.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6" +checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.76", ] [[package]] name = "zeroize" -version = "1.7.0" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "525b4ec142c6b68a2d10f01f7bbf6755599ca3f81ea53b8431b7dd348f5fdb2d" +checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" diff --git a/Cargo.toml b/Cargo.toml index dfab15f..9d27e12 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -28,13 +28,17 @@ git-next-forge-github = { path = "crates/forge-github", version = "0.13" } # TUI ratatui = "0.28" +directories = "5.0.1" +lazy_static = "1.5.0" +color-eyre = "0.6.3" # CLI parsing clap = { version = "4.5", features = ["cargo", "derive"] } # logging tracing = "0.1" -tracing-subscriber = "0.3" +tracing-subscriber = { version = "0.3", features = ["env-filter"] } +tracing-error = "0.2.0" # base64 decoding base64 = "0.22" diff --git a/crates/cli/Cargo.toml b/crates/cli/Cargo.toml index afa3cac..2871162 100644 --- a/crates/cli/Cargo.toml +++ b/crates/cli/Cargo.toml @@ -12,10 +12,11 @@ keywords = { workspace = true } categories = { workspace = true } [features] -default = ["forgejo", "github"] +# default = ["forgejo", "github"] +default = ["forgejo", "github", "tui"] forgejo = ["git-next-forge-forgejo"] github = ["git-next-forge-github"] -tui = ["ratatui"] +tui = ["ratatui", "directories", "lazy_static"] [dependencies] git-next-core = { workspace = true } @@ -24,6 +25,9 @@ git-next-forge-github = { workspace = true, optional = true } # TUI ratatui = { workspace = true, optional = true } +directories = { workspace = true, optional = true } +lazy_static = { workspace = true, optional = true } +color-eyre = { workspace = true } # CLI parsing clap = { workspace = true } @@ -34,6 +38,7 @@ kxio = { workspace = true } # logging tracing = { workspace = true } tracing-subscriber = { workspace = true } +tracing-error.workspace = true # Conventional Commit check git-conventional = { workspace = true } diff --git a/crates/cli/src/init.rs b/crates/cli/src/init.rs index a13a10a..6b6cbca 100644 --- a/crates/cli/src/init.rs +++ b/crates/cli/src/init.rs @@ -1,5 +1,5 @@ // -use anyhow::{Context, Result}; +use color_eyre::{eyre::Context, Result}; use kxio::fs::FileSystem; pub fn run(fs: &FileSystem) -> Result<()> { diff --git a/crates/cli/src/main.rs b/crates/cli/src/main.rs index f054c2c..b93a3df 100644 --- a/crates/cli/src/main.rs +++ b/crates/cli/src/main.rs @@ -1,4 +1,6 @@ // +#![allow(clippy::module_name_repetitions)] + mod alerts; mod file_watcher; mod forge; @@ -17,8 +19,8 @@ use git_next_core::git; use std::path::PathBuf; -use anyhow::Result; use clap::Parser; +use color_eyre::Result; use kxio::{fs, network::Network}; #[derive(Parser, Debug)] diff --git a/crates/cli/src/repo/branch.rs b/crates/cli/src/repo/branch.rs index 3bd4cde..dfe4da4 100644 --- a/crates/cli/src/repo/branch.rs +++ b/crates/cli/src/repo/branch.rs @@ -17,15 +17,13 @@ use tracing::{info, instrument, warn}; // advance next to the next commit towards the head of the dev branch #[instrument(fields(next), skip_all)] pub fn advance_next( - next: &Commit, - main: &Commit, - dev_commit_history: &[Commit], + commit: Option, + force: git_next_core::git::push::Force, repo_details: RepoDetails, repo_config: RepoConfig, open_repository: &dyn OpenRepositoryLike, message_token: MessageToken, ) -> Result { - let (commit, force) = find_next_commit_on_dev(next, main, dev_commit_history); let commit = commit.ok_or_else(|| Error::NextAtDev)?; validate_commit_message(commit.message())?; info!("Advancing next to commit '{}'", commit); diff --git a/crates/cli/src/repo/handlers/advance_main.rs b/crates/cli/src/repo/handlers/advance_main.rs index 8e25008..fcd4a50 100644 --- a/crates/cli/src/repo/handlers/advance_main.rs +++ b/crates/cli/src/repo/handlers/advance_main.rs @@ -5,11 +5,14 @@ use git_next_core::RepoConfigSource; use tracing::warn; -use crate::repo::{ - branch::advance_main, - do_send, - messages::{AdvanceMain, LoadConfigFromRepo, ValidateRepo}, - RepoActor, +use crate::{ + repo::{ + branch::advance_main, + do_send, + messages::{AdvanceMain, LoadConfigFromRepo, ValidateRepo}, + RepoActor, + }, + server::actor::messages::RepoUpdate, }; impl Handler for RepoActor { @@ -26,13 +29,13 @@ impl Handler for RepoActor { let repo_details = self.repo_details.clone(); let addr = ctx.address(); let message_token = self.message_token; + let commit = msg.unwrap(); - match advance_main( - msg.unwrap(), - &repo_details, - &repo_config, - &**open_repository, - ) { + self.update_tui(RepoUpdate::AdvancingMain { + commit: commit.clone(), + }); + + match advance_main(commit, &repo_details, &repo_config, &**open_repository) { Err(err) => { warn!("advance main: {err}"); } diff --git a/crates/cli/src/repo/handlers/advance_next.rs b/crates/cli/src/repo/handlers/advance_next.rs index 747db94..1d90fcc 100644 --- a/crates/cli/src/repo/handlers/advance_next.rs +++ b/crates/cli/src/repo/handlers/advance_next.rs @@ -3,11 +3,14 @@ use actix::prelude::*; use tracing::warn; -use crate::repo::{ - branch::advance_next, - do_send, - messages::{AdvanceNext, AdvanceNextPayload, ValidateRepo}, - RepoActor, +use crate::{ + repo::{ + branch::{advance_next, find_next_commit_on_dev}, + do_send, + messages::{AdvanceNext, AdvanceNextPayload, ValidateRepo}, + RepoActor, + }, + server::actor::messages::RepoUpdate, }; impl Handler for RepoActor { @@ -20,6 +23,7 @@ impl Handler for RepoActor { let Some(open_repository) = &self.open_repository else { return; }; + let AdvanceNextPayload { next, main, @@ -29,10 +33,15 @@ impl Handler for RepoActor { let repo_config = repo_config.clone(); let addr = ctx.address(); + let (commit, force) = find_next_commit_on_dev(&next, &main, &dev_commit_history); + self.update_tui(RepoUpdate::AdvancingNext { + commit: commit.clone(), + force: force.clone(), + }); + match advance_next( - &next, - &main, - &dev_commit_history, + commit, + force, repo_details, repo_config, &**open_repository, diff --git a/crates/cli/src/repo/handlers/check_ci_status.rs b/crates/cli/src/repo/handlers/check_ci_status.rs index 122f019..8128998 100644 --- a/crates/cli/src/repo/handlers/check_ci_status.rs +++ b/crates/cli/src/repo/handlers/check_ci_status.rs @@ -3,10 +3,13 @@ use actix::prelude::*; use tracing::{debug, Instrument as _}; -use crate::repo::{ - do_send, - messages::{CheckCIStatus, ReceiveCIStatus}, - RepoActor, +use crate::{ + repo::{ + do_send, + messages::{CheckCIStatus, ReceiveCIStatus}, + RepoActor, + }, + server::actor::messages::RepoUpdate, }; impl Handler for RepoActor { @@ -14,11 +17,13 @@ impl Handler for RepoActor { fn handle(&mut self, msg: CheckCIStatus, ctx: &mut Self::Context) -> Self::Result { crate::repo::logger(self.log.as_ref(), "start: CheckCIStatus"); + let addr = ctx.address(); let forge = self.forge.duplicate(); let next = msg.unwrap(); let log = self.log.clone(); + self.update_tui(RepoUpdate::CheckingCI); // get the status - pass, fail, pending (all others map to fail, e.g. error) async move { let status = forge.commit_status(&next).await; diff --git a/crates/cli/src/repo/handlers/clone_repo.rs b/crates/cli/src/repo/handlers/clone_repo.rs index a851c69..642c2b8 100644 --- a/crates/cli/src/repo/handlers/clone_repo.rs +++ b/crates/cli/src/repo/handlers/clone_repo.rs @@ -4,10 +4,13 @@ use actix::prelude::*; use git_next_core::git; use tracing::{debug, instrument, warn}; -use crate::repo::{ - do_send, logger, - messages::{CloneRepo, LoadConfigFromRepo, RegisterWebhook}, - RepoActor, +use crate::{ + repo::{ + do_send, logger, + messages::{CloneRepo, LoadConfigFromRepo, RegisterWebhook}, + RepoActor, + }, + server::actor::messages::RepoUpdate, }; impl Handler for RepoActor { @@ -15,11 +18,13 @@ impl Handler for RepoActor { #[instrument(name = "RepoActor::CloneRepo", skip_all, fields(repo = %self.repo_details))] fn handle(&mut self, _msg: CloneRepo, ctx: &mut Self::Context) -> Self::Result { logger(self.log.as_ref(), "Handler: CloneRepo: start"); + self.update_tui(RepoUpdate::Opening); debug!("Handler: CloneRepo: start"); match git::repository::open(&*self.repository_factory, &self.repo_details) { Ok(repository) => { logger(self.log.as_ref(), "open okay"); debug!("open okay"); + self.update_tui(RepoUpdate::Opened); self.open_repository.replace(repository); if self.repo_details.repo_config.is_none() { do_send(&ctx.address(), LoadConfigFromRepo, self.log.as_ref()); @@ -30,6 +35,7 @@ impl Handler for RepoActor { Err(err) => { logger(self.log.as_ref(), "open failed"); warn!("Could not open repo: {err:?}"); + self.alert_tui(err.to_string()); } } debug!("Handler: CloneRepo: finish"); diff --git a/crates/cli/src/repo/handlers/load_config_from_repo.rs b/crates/cli/src/repo/handlers/load_config_from_repo.rs index dd3567b..de0da29 100644 --- a/crates/cli/src/repo/handlers/load_config_from_repo.rs +++ b/crates/cli/src/repo/handlers/load_config_from_repo.rs @@ -5,10 +5,13 @@ use git_next_core::git::UserNotification; use tracing::{debug, instrument, Instrument as _}; -use crate::repo::{ - do_send, load, - messages::{LoadConfigFromRepo, ReceiveRepoConfig}, - notify_user, RepoActor, +use crate::{ + repo::{ + do_send, load, + messages::{LoadConfigFromRepo, ReceiveRepoConfig}, + notify_user, RepoActor, + }, + server::actor::messages::RepoUpdate, }; impl Handler for RepoActor { @@ -16,6 +19,7 @@ impl Handler for RepoActor { #[instrument(name = "Repocrate::repo::LoadConfigFromRepo", skip_all, fields(repo = %self.repo_details))] fn handle(&mut self, _msg: LoadConfigFromRepo, ctx: &mut Self::Context) -> Self::Result { debug!("Handler: LoadConfigFromRepo: start"); + self.update_tui(RepoUpdate::LoadingConfigFromRepo); let Some(open_repository) = &self.open_repository else { return; }; diff --git a/crates/cli/src/repo/handlers/receive_ci_status.rs b/crates/cli/src/repo/handlers/receive_ci_status.rs index 6d8e272..1294f67 100644 --- a/crates/cli/src/repo/handlers/receive_ci_status.rs +++ b/crates/cli/src/repo/handlers/receive_ci_status.rs @@ -4,10 +4,13 @@ use actix::prelude::*; use git_next_core::git::{forge::commit::Status, graph, UserNotification}; use tracing::debug; -use crate::repo::{ - delay_send, do_send, logger, - messages::{AdvanceMain, ReceiveCIStatus, ValidateRepo}, - notify_user, RepoActor, +use crate::{ + repo::{ + delay_send, do_send, logger, + messages::{AdvanceMain, ReceiveCIStatus, ValidateRepo}, + notify_user, RepoActor, + }, + server::actor::messages::RepoUpdate, }; impl Handler for RepoActor { @@ -22,6 +25,11 @@ impl Handler for RepoActor { let repo_alias = self.repo_details.repo_alias.clone(); let message_token = self.message_token; let sleep_duration = self.sleep_duration; + let graph_log = graph::log(&self.repo_details); + self.update_tui_log(graph_log.clone()); + self.update_tui(RepoUpdate::ReceiveCIStatus { + status: status.clone(), + }); debug!(?status, ""); match status { @@ -34,13 +42,14 @@ impl Handler for RepoActor { } Status::Fail => { tracing::warn!("Checks have failed"); + notify_user( self.notify_user_recipient.as_ref(), UserNotification::CICheckFailed { forge_alias, repo_alias, commit: next, - log: graph::log(&self.repo_details), + log: graph_log, }, log.as_ref(), ); diff --git a/crates/cli/src/repo/handlers/receive_repo_config.rs b/crates/cli/src/repo/handlers/receive_repo_config.rs index 12934d8..c496be3 100644 --- a/crates/cli/src/repo/handlers/receive_repo_config.rs +++ b/crates/cli/src/repo/handlers/receive_repo_config.rs @@ -2,10 +2,13 @@ use actix::prelude::*; use tracing::instrument; -use crate::repo::{ - do_send, - messages::{ReceiveRepoConfig, RegisterWebhook}, - RepoActor, +use crate::{ + repo::{ + do_send, + messages::{ReceiveRepoConfig, RegisterWebhook}, + RepoActor, + }, + server::actor::messages::RepoUpdate, }; impl Handler for RepoActor { @@ -13,8 +16,11 @@ impl Handler for RepoActor { #[instrument(name = "RepoActor::ReceiveRepoConfig", skip_all, fields(repo = %self.repo_details, branches = ?msg))] fn handle(&mut self, msg: ReceiveRepoConfig, ctx: &mut Self::Context) -> Self::Result { let repo_config = msg.unwrap(); + self.update_tui(RepoUpdate::ReceiveRepoConfig { + repo_config: repo_config.clone(), + }); self.repo_details.repo_config.replace(repo_config); - + self.update_tui_branches(); do_send(&ctx.address(), RegisterWebhook::new(), self.log.as_ref()); } } diff --git a/crates/cli/src/repo/handlers/register_webhook.rs b/crates/cli/src/repo/handlers/register_webhook.rs index 4981918..8e6704f 100644 --- a/crates/cli/src/repo/handlers/register_webhook.rs +++ b/crates/cli/src/repo/handlers/register_webhook.rs @@ -1,12 +1,15 @@ // use actix::prelude::*; -use tracing::{debug, Instrument as _}; +use tracing::{debug, error, Instrument as _}; -use crate::repo::{ - do_send, - messages::{RegisterWebhook, WebhookRegistered}, - notify_user, RepoActor, +use crate::{ + repo::{ + do_send, + messages::{RegisterWebhook, WebhookRegistered}, + notify_user, RepoActor, + }, + server::actor::messages::RepoUpdate, }; use git_next_core::git::UserNotification; @@ -25,11 +28,12 @@ impl Handler for RepoActor { let addr = ctx.address(); let notify_user_recipient = self.notify_user_recipient.clone(); let log = self.log.clone(); + self.update_tui(RepoUpdate::RegisteringWebhook); debug!("registering webhook"); async move { match forge.register_webhook(&repo_listen_url).await { Ok(registered_webhook) => { - debug!(?registered_webhook, ""); + debug!(?registered_webhook, "webhook registered"); do_send( &addr, WebhookRegistered::from(registered_webhook), @@ -37,6 +41,7 @@ impl Handler for RepoActor { ); } Err(err) => { + error!(?err, "failed to register webhook"); notify_user( notify_user_recipient.as_ref(), UserNotification::WebhookRegistration { @@ -52,6 +57,8 @@ impl Handler for RepoActor { .in_current_span() .into_actor(self) .wait(ctx); + } else { + self.alert_tui("already have a webhook id - cant register webhook"); } } } diff --git a/crates/cli/src/repo/handlers/unregister_webhook.rs b/crates/cli/src/repo/handlers/unregister_webhook.rs index 8c748ac..fcc7725 100644 --- a/crates/cli/src/repo/handlers/unregister_webhook.rs +++ b/crates/cli/src/repo/handlers/unregister_webhook.rs @@ -3,13 +3,17 @@ use actix::prelude::*; use tracing::{debug, warn, Instrument as _}; -use crate::repo::{messages::UnRegisterWebhook, RepoActor}; +use crate::{ + repo::{messages::UnRegisterWebhook, RepoActor}, + server::actor::messages::RepoUpdate, +}; impl Handler for RepoActor { type Result = (); fn handle(&mut self, _msg: UnRegisterWebhook, ctx: &mut Self::Context) -> Self::Result { if let Some(webhook_id) = self.webhook_id.take() { + self.update_tui(RepoUpdate::UnregisteringWebhook); let forge = self.forge.duplicate(); debug!("unregistering webhook"); async move { diff --git a/crates/cli/src/repo/handlers/validate_repo.rs b/crates/cli/src/repo/handlers/validate_repo.rs index a693140..2e77d3f 100644 --- a/crates/cli/src/repo/handlers/validate_repo.rs +++ b/crates/cli/src/repo/handlers/validate_repo.rs @@ -1,15 +1,21 @@ // use actix::prelude::*; -use tracing::{debug, instrument, Instrument as _}; +use tracing::{debug, info, instrument, Instrument as _}; -use crate::repo::{ - do_send, logger, - messages::{AdvanceNext, AdvanceNextPayload, CheckCIStatus, MessageToken, ValidateRepo}, - notify_user, RepoActor, +use crate::{ + repo::{ + do_send, logger, + messages::{AdvanceNext, AdvanceNextPayload, CheckCIStatus, MessageToken, ValidateRepo}, + notify_user, RepoActor, + }, + server::actor::messages::RepoUpdate, }; -use git_next_core::git::validation::positions::{validate, Error, Positions}; +use git_next_core::git::{ + self, + validation::positions::{validate, Error, Positions}, +}; impl Handler for RepoActor { type Result = (); @@ -41,19 +47,34 @@ impl Handler for RepoActor { format!("accepted token: {}", self.message_token), ); + self.update_tui(RepoUpdate::ValidateRepo); + // Repository positions let Some(ref open_repository) = self.open_repository else { logger(self.log.as_ref(), "no open repository"); + self.alert_tui("repo not open"); return; }; logger(self.log.as_ref(), "have open repository"); let Some(repo_config) = self.repo_details.repo_config.clone() else { logger(self.log.as_ref(), "no repo config"); + self.alert_tui("no repo config"); return; }; logger(self.log.as_ref(), "have repo config"); - match validate(&**open_repository, &self.repo_details, &repo_config) { + info!("collecting git graph log"); + let git_log = git::graph::log(&self.repo_details); + info!(?git_log, "collected git graph log"); + self.update_tui_log(git_log.clone()); + info!("sent to ui git graph log"); + + match validate( + &**open_repository, + &self.repo_details, + &repo_config, + git_log, + ) { Ok(Positions { main, next, @@ -75,10 +96,11 @@ impl Handler for RepoActor { self.log.as_ref(), ); } else { - // do nothing + self.update_tui(RepoUpdate::Okay { main, next, dev }); } } Err(Error::Retryable(message)) => { + self.alert_tui(format!("[retryable: {message}]")); logger(self.log.as_ref(), message); let addr = ctx.address(); let message_token = self.message_token; @@ -95,12 +117,16 @@ impl Handler for RepoActor { .into_actor(self) .wait(ctx); } - Err(Error::UserIntervention(user_notification)) => notify_user( - self.notify_user_recipient.as_ref(), - user_notification, - self.log.as_ref(), - ), + Err(Error::UserIntervention(user_notification)) => { + self.alert_tui(format!("[USER INTERVENTION: {user_notification}]")); + notify_user( + self.notify_user_recipient.as_ref(), + user_notification, + self.log.as_ref(), + ); + } Err(Error::NonRetryable(message)) => { + self.alert_tui(format!("[Error: {message}]")); logger(self.log.as_ref(), message); } } diff --git a/crates/cli/src/repo/handlers/webhook_notification.rs b/crates/cli/src/repo/handlers/webhook_notification.rs index 930d61e..f5fb83d 100644 --- a/crates/cli/src/repo/handlers/webhook_notification.rs +++ b/crates/cli/src/repo/handlers/webhook_notification.rs @@ -3,10 +3,13 @@ use actix::prelude::*; use tracing::{info, instrument, warn}; -use crate::repo::{ - do_send, logger, - messages::{ValidateRepo, WebhookNotification}, - ActorLog, RepoActor, +use crate::{ + repo::{ + do_send, logger, + messages::{ValidateRepo, WebhookNotification}, + ActorLog, RepoActor, + }, + server::actor::messages::RepoUpdate, }; use git_next_core::{ @@ -52,6 +55,10 @@ impl Handler for RepoActor { return; } Some(Branch::Main) => { + self.update_tui(RepoUpdate::WebhookReceived { + branch: Branch::Main, + push: push.clone(), + }); if handle_push( push, &config.branches().main(), @@ -64,6 +71,10 @@ impl Handler for RepoActor { }; } Some(Branch::Next) => { + self.update_tui(RepoUpdate::WebhookReceived { + branch: Branch::Next, + push: push.clone(), + }); if handle_push( push, &config.branches().next(), @@ -76,6 +87,10 @@ impl Handler for RepoActor { }; } Some(Branch::Dev) => { + self.update_tui(RepoUpdate::WebhookReceived { + branch: Branch::Dev, + push: push.clone(), + }); if handle_push( push, &config.branches().dev(), @@ -135,7 +150,7 @@ fn handle_push( last_commit: &mut Option, log: Option<&ActorLog>, ) -> Result<(), ()> { - logger(log, "message is for dev branch"); + logger(log, format!("message is for {branch} branch")); let commit = Commit::from(push); if last_commit.as_ref() == Some(&commit) { logger(log, format!("not a new commit on {branch}")); diff --git a/crates/cli/src/repo/handlers/webhook_registered.rs b/crates/cli/src/repo/handlers/webhook_registered.rs index a3a5aae..dbb03ff 100644 --- a/crates/cli/src/repo/handlers/webhook_registered.rs +++ b/crates/cli/src/repo/handlers/webhook_registered.rs @@ -2,16 +2,20 @@ use actix::prelude::*; use tracing::instrument; -use crate::repo::{ - do_send, - messages::{ValidateRepo, WebhookRegistered}, - RepoActor, +use crate::{ + repo::{ + do_send, + messages::{ValidateRepo, WebhookRegistered}, + RepoActor, + }, + server::actor::messages::RepoUpdate, }; impl Handler for RepoActor { type Result = (); #[instrument(name = "RepoActor::WebhookRegistered", skip_all, fields(repo = %self.repo_details, webhook_id = %msg.webhook_id()))] fn handle(&mut self, msg: WebhookRegistered, ctx: &mut Self::Context) -> Self::Result { + self.update_tui(RepoUpdate::RegisteredWebhook); self.webhook_id.replace(msg.webhook_id().clone()); self.webhook_auth.replace(msg.webhook_auth().clone()); do_send( diff --git a/crates/cli/src/repo/mod.rs b/crates/cli/src/repo/mod.rs index 7a0c3bc..37aef21 100644 --- a/crates/cli/src/repo/mod.rs +++ b/crates/cli/src/repo/mod.rs @@ -1,16 +1,23 @@ // use actix::prelude::*; -use crate::alerts::messages::NotifyUser; +use crate::{ + alerts::messages::NotifyUser, + server::{ + actor::messages::{RepoUpdate, ServerUpdate}, + ServerActor, + }, +}; use derive_more::Deref; use kxio::network::Network; use std::time::Duration; -use tracing::{info, warn, Instrument}; +use tracing::{info, instrument, warn, Instrument}; use git_next_core::{ git::{ self, repository::{factory::RepositoryFactory, open::OpenRepositoryLike}, + validation::positions::get_commit_histories, UserNotification, }, server::ListenUrl, @@ -59,6 +66,7 @@ pub struct RepoActor { forge: Box, log: Option, notify_user_recipient: Option>, + server_addr: Option>, } impl RepoActor { #[allow(clippy::too_many_arguments)] @@ -71,6 +79,7 @@ impl RepoActor { repository_factory: Box, sleep_duration: std::time::Duration, notify_user_recipient: Option>, + server_addr: Option>, ) -> Self { let message_token = messages::MessageToken::default(); Self { @@ -90,6 +99,62 @@ impl RepoActor { sleep_duration, log: None, notify_user_recipient, + server_addr, + } + } + + fn update_tui_branches(&self) { + #[cfg(feature = "tui")] + { + use crate::server::actor::messages::RepoUpdate; + let Some(repo_config) = &self.repo_details.repo_config else { + return; + }; + let Some(open_repo) = self.open_repository.as_ref() else { + return; + }; + let branches = repo_config.branches().clone(); + let histories = get_commit_histories(&**open_repo, repo_config).ok(); + self.update_tui(RepoUpdate::Branches { + branches, + histories, + }); + } + } + + #[instrument] + fn update_tui_log(&self, log: git::graph::Log) { + info!("ready to send log"); + #[cfg(feature = "tui")] + { + info!("sending..."); + self.update_tui(RepoUpdate::Log { log }); + info!("sent"); + } + } + + fn alert_tui(&self, alert: impl Into) { + #[cfg(feature = "tui")] + { + self.update_tui(RepoUpdate::Alert { + alert: alert.into(), + }); + } + } + + fn update_tui(&self, repo_update: RepoUpdate) { + #[cfg(feature = "tui")] + { + let Some(server_addr) = &self.server_addr else { + return; + }; + + let update = ServerUpdate::RepoUpdate { + forge_alias: self.repo_details.forge.forge_alias().clone(), + repo_alias: self.repo_details.repo_alias.clone(), + repo_update, + }; + server_addr.do_send(update); } } } diff --git a/crates/cli/src/repo/tests/branch/advance_next.rs b/crates/cli/src/repo/tests/branch/advance_next.rs index cddc886..9425700 100644 --- a/crates/cli/src/repo/tests/branch/advance_next.rs +++ b/crates/cli/src/repo/tests/branch/advance_next.rs @@ -1,6 +1,28 @@ // +use crate::repo::branch::find_next_commit_on_dev; + use super::*; +fn advance_next_sut( + next: &Commit, + main: &Commit, + dev_commit_history: &[Commit], + repo_details: RepoDetails, + repo_config: RepoConfig, + open_repository: &dyn OpenRepositoryLike, + message_token: MessageToken, +) -> branch::Result { + let (commit, force) = find_next_commit_on_dev(next, main, dev_commit_history); + branch::advance_next( + commit, + force, + repo_details, + repo_config, + open_repository, + message_token, + ) +} + mod when_at_dev { // next and dev branches are the same use super::*; @@ -16,7 +38,7 @@ mod when_at_dev { // no on_push defined - so any call to push will cause an error let message_token = given::a_message_token(); let_assert!( - Err(err) = branch::advance_next( + Err(err) = advance_next_sut( &next, main, dev_commit_history, @@ -51,7 +73,7 @@ mod can_advance { // no on_push defined - so any call to push will cause an error let message_token = given::a_message_token(); let_assert!( - Err(err) = branch::advance_next( + Err(err) = advance_next_sut( &next, main, dev_commit_history, @@ -82,7 +104,7 @@ mod can_advance { // no on_push defined - so any call to push will cause an error let message_token = given::a_message_token(); let_assert!( - Err(err) = branch::advance_next( + Err(err) = advance_next_sut( &next, main, dev_commit_history, @@ -122,7 +144,7 @@ mod can_advance { expect::push(&mut open_repository, Err(git::push::Error::Lock)); let message_token = given::a_message_token(); let_assert!( - Err(err) = branch::advance_next( + Err(err) = advance_next_sut( &next, main, dev_commit_history, @@ -154,7 +176,7 @@ mod can_advance { expect::push_ok(&mut open_repository); let message_token = given::a_message_token(); let_assert!( - Ok(mt) = branch::advance_next( + Ok(mt) = advance_next_sut( &next, main, dev_commit_history, diff --git a/crates/cli/src/repo/tests/given.rs b/crates/cli/src/repo/tests/given.rs index e8da64e..c0a6b91 100644 --- a/crates/cli/src/repo/tests/given.rs +++ b/crates/cli/src/repo/tests/given.rs @@ -195,6 +195,7 @@ pub fn a_repo_actor( repository_factory, std::time::Duration::from_nanos(1), None, + None, ) .with_log(actors_log), log, diff --git a/crates/cli/src/server/actor/handlers/mod.rs b/crates/cli/src/server/actor/handlers/mod.rs index 3633000..a5e6358 100644 --- a/crates/cli/src/server/actor/handlers/mod.rs +++ b/crates/cli/src/server/actor/handlers/mod.rs @@ -1,5 +1,6 @@ mod file_updated; mod receive_app_config; mod receive_valid_app_config; +mod server_update; mod shutdown; mod subscribe_updates; diff --git a/crates/cli/src/server/actor/handlers/receive_valid_app_config.rs b/crates/cli/src/server/actor/handlers/receive_valid_app_config.rs index 6380c67..1028cab 100644 --- a/crates/cli/src/server/actor/handlers/receive_valid_app_config.rs +++ b/crates/cli/src/server/actor/handlers/receive_valid_app_config.rs @@ -8,7 +8,7 @@ use crate::{ alerts::messages::UpdateShout, repo::{messages::CloneRepo, RepoActor}, server::actor::{ - messages::{ReceiveValidAppConfig, ValidAppConfig}, + messages::{ReceiveValidAppConfig, ServerUpdate, ValidAppConfig}, ServerActor, }, webhook::{ @@ -21,7 +21,7 @@ use crate::{ impl Handler for ServerActor { type Result = (); - fn handle(&mut self, msg: ReceiveValidAppConfig, _ctx: &mut Self::Context) -> Self::Result { + fn handle(&mut self, msg: ReceiveValidAppConfig, ctx: &mut Self::Context) -> Self::Result { let ValidAppConfig { app_config, socket_address, @@ -37,6 +37,7 @@ impl Handler for ServerActor { let webhook_router = WebhookRouterActor::default().start(); let listen_url = app_config.listen().url(); let notify_user_recipient = self.alerts.clone().recipient(); + let server_addr = Some(ctx.address()); // Forge Actors for (forge_alias, forge_config) in app_config.forges() { let repo_actors = self @@ -46,6 +47,7 @@ impl Handler for ServerActor { &server_storage, listen_url, ¬ify_user_recipient, + server_addr.clone(), ) .into_iter() .map(start_repo_actor) @@ -69,9 +71,18 @@ impl Handler for ServerActor { WebhookActor::new(socket_address, webhook_router.recipient()).start(); self.webhook_actor_addr.replace(webhook_actor_addr); let shout = app_config.shout().clone(); - self.app_config.replace(app_config); + self.app_config.replace(app_config.clone()); + self.do_send( + ServerUpdate::AppConfigLoaded { + app_config: ValidAppConfig { + app_config, + socket_address, + storage: server_storage, + }, + }, + ctx, + ); self.alerts.do_send(UpdateShout::new(shout)); - self.send_server_updates(); } } diff --git a/crates/cli/src/server/actor/handlers/server_update.rs b/crates/cli/src/server/actor/handlers/server_update.rs new file mode 100644 index 0000000..0d5624d --- /dev/null +++ b/crates/cli/src/server/actor/handlers/server_update.rs @@ -0,0 +1,14 @@ +use actix::Handler; + +// +use crate::server::{actor::messages::ServerUpdate, ServerActor}; + +impl Handler for ServerActor { + type Result = (); + + fn handle(&mut self, msg: ServerUpdate, _ctx: &mut Self::Context) -> Self::Result { + self.subscribers.iter().for_each(move |subscriber| { + subscriber.do_send(msg.clone()); + }); + } +} diff --git a/crates/cli/src/server/actor/messages.rs b/crates/cli/src/server/actor/messages.rs index d361890..4a6ae85 100644 --- a/crates/cli/src/server/actor/messages.rs +++ b/crates/cli/src/server/actor/messages.rs @@ -1,12 +1,13 @@ +// use actix::{Message, Recipient}; -//- use derive_more::Constructor; use git_next_core::{ - git::graph::Log, + git::{self, forge::commit::Status, graph::Log, Commit}, message, server::{AppConfig, Storage}, - ForgeAlias, RepoAlias, RepoBranches, + webhook::{push::Branch, Push}, + ForgeAlias, RepoAlias, RepoBranches, RepoConfig, }; use std::net::SocketAddr; @@ -40,20 +41,58 @@ message!(Shutdown, "Notification to shutdown the server actor"); #[derive(Clone, Debug, PartialEq, Eq, Message)] #[rtype(result = "()")] pub enum ServerUpdate { - /// Status of a repo - UpdateRepoSummary { + /// List of all configured forges and aliases + AppConfigLoaded { app_config: ValidAppConfig }, + + RepoUpdate { forge_alias: ForgeAlias, repo_alias: RepoAlias, + repo_update: RepoUpdate, + }, +} + +#[derive(Clone, Debug, PartialEq, Eq)] +pub enum RepoUpdate { + Branches { branches: RepoBranches, + histories: Option, + }, + Log { log: Log, }, - /// remove a repo - RemoveRepo { - forge_alias: ForgeAlias, - repo_alias: RepoAlias, + ValidateRepo, + Okay { + main: Commit, + next: Commit, + dev: Commit, }, - /// test message - Ping, + Alert { + alert: String, + }, + CheckingCI, + AdvancingNext { + commit: Option, + force: git::push::Force, + }, + AdvancingMain { + commit: git::Commit, + }, + Opening, + LoadingConfigFromRepo, + ReceiveCIStatus { + status: Status, + }, + ReceiveRepoConfig { + repo_config: RepoConfig, + }, + RegisteringWebhook, + UnregisteringWebhook, + WebhookReceived { + branch: Branch, + push: Push, + }, + RegisteredWebhook, + Opened, } message!( diff --git a/crates/cli/src/server/actor/mod.rs b/crates/cli/src/server/actor/mod.rs index d87ebe8..74eefab 100644 --- a/crates/cli/src/server/actor/mod.rs +++ b/crates/cli/src/server/actor/mod.rs @@ -118,6 +118,7 @@ impl ServerActor { server_storage: &Storage, listen_url: &ListenUrl, notify_user_recipient: &Recipient, + server_addr: Option>, ) -> Vec<(ForgeAlias, RepoAlias, RepoActor)> { let span = tracing::info_span!("create_forge_repos", name = %forge_name, config = %forge_config); @@ -125,8 +126,13 @@ impl ServerActor { let _guard = span.enter(); tracing::info!("Creating Forge"); let mut repos = vec![]; - let creator = - self.create_actor(forge_name, forge_config.clone(), server_storage, listen_url); + let creator = self.create_actor( + forge_name, + forge_config.clone(), + server_storage, + listen_url, + server_addr, + ); for (repo_alias, server_repo_config) in forge_config.repos() { let forge_repo = creator(( repo_alias, @@ -148,6 +154,7 @@ impl ServerActor { forge_config: ForgeConfig, server_storage: &Storage, listen_url: &ListenUrl, + server_addr: Option>, ) -> impl Fn( (RepoAlias, &ServerRepoConfig, Recipient), ) -> (ForgeAlias, RepoAlias, RepoActor) { @@ -194,6 +201,7 @@ impl ServerActor { repository_factory.duplicate(), sleep_duration, Some(notify_user_recipient), + server_addr.clone(), ); (forge_name.clone(), repo_alias, actor) } @@ -242,10 +250,4 @@ impl ServerActor { ctx.address().do_send(msg); } } - - fn send_server_updates(&self) { - self.subscribers.iter().for_each(|subscriber| { - subscriber.do_send(ServerUpdate::Ping); - }); - } } diff --git a/crates/cli/src/server/mod.rs b/crates/cli/src/server/mod.rs index 211304c..31ff89c 100644 --- a/crates/cli/src/server/mod.rs +++ b/crates/cli/src/server/mod.rs @@ -17,7 +17,7 @@ pub use actor::ServerActor; use git_next_core::git::RepositoryFactory; -use anyhow::{Context, Result}; +use color_eyre::{eyre::Context, Result}; use kxio::{fs::FileSystem, network::Network}; use tracing::info; @@ -48,7 +48,12 @@ pub fn start( repo: Box, sleep_duration: std::time::Duration, ) -> Result<()> { - if !ui { + if ui { + #[cfg(feature = "tui")] + { + crate::tui::logging::initialize_logging()?; + } + } else { init_logging(); } @@ -59,7 +64,6 @@ pub fn start( info!("Starting Server..."); let server = ServerActor::new(fs.clone(), net.clone(), alerts_addr, repo, sleep_duration).start(); - server.do_send(FileUpdated); info!("Starting File Watcher..."); #[allow(clippy::expect_used)] @@ -75,18 +79,18 @@ pub fn start( let (tx_shutdown, rx_shutdown) = channel::<()>(); let tui_addr = tui::Tui::new(tx_shutdown).start(); - // tui_addr.do_send(tui::Tick); - let _ = tui_addr.send(tui::Tick).await; server.do_send(SubscribeToUpdates::new(tui_addr.clone().recipient())); + server.do_send(FileUpdated); // update file after ui subscription in place loop { let _ = tui_addr.send(tui::Tick).await; if rx_shutdown.try_recv().is_ok() { break; } - // actix_rt::task::yield_now().await; + actix_rt::time::sleep(Duration::from_millis(16)).await; } } } else { + server.do_send(FileUpdated); info!("Server running - Press Ctrl-C to stop..."); let _ = signal::ctrl_c().await; info!("Ctrl-C received, shutting down..."); @@ -95,7 +99,7 @@ pub fn start( // shutdown fw_shutdown.store(true, Ordering::Relaxed); server.do_send(crate::server::actor::messages::Shutdown); - actix_rt::time::sleep(std::time::Duration::from_millis(200)).await; + actix_rt::time::sleep(std::time::Duration::from_millis(10)).await; System::current().stop(); }; diff --git a/crates/cli/src/tui/README.md b/crates/cli/src/tui/README.md new file mode 100644 index 0000000..bafa379 --- /dev/null +++ b/crates/cli/src/tui/README.md @@ -0,0 +1,121 @@ +# TUI Actor + +- Maintains it's own copy of data for holding state +- Is notified of update via actor messages from the Server and Repo Actors +- State is somewhat heirarchical + +## State + +```rust +enum TuiState { + Initial, + Configured { + app_config: ValidAppConfig, + forges: BTreeMap, + }, +} +enum RepoState { + Identified { repo_alias: RepoAlias }, + Configured { repo_alias: RepoAlias, branches: RepoBranches }, + Ready { repo_alias: RepoAlias, branches: RepoBranches, main: Commit, next: Commit, dev: Commit, log: Log }, + Alert { repo_alias: RepoAlias, message: String, branches: RepoBranches, main: Commit, next: Commit, dev: Commit, log: Log }, +} +``` + +## State Transitions: + +### `TuiState` + +```mermaid +stateDiagram-v2 + * --> Initial + Initial --> Configured +``` + +- `message!(Configure, ValidAppConfig, "Initialise UI with valid config");` + +### `RepoState` + +```mermaid +stateDiagram-v2 + * --> Identified + Identified --> Configured + Identified --> Ready + Configured --> Ready + Ready --> Alert + Configured --> Alert + Identified --> Alert + Alert --> Ready +``` + +- `Identified` - from AppConfig where a repo alias is listed, but repo config needs to be loaded from `.git-next.toml` +- `Configured` - as `Identified` but either branches are identified in server config, OR the `.git-next.toml` file has been loaded +- `Ready` - as `Configured` but the branch positions have been validated and do not require user intervention +- `Alert` - as `Ready` but user intervention is required + +## Widget + +Initial mock up of UI. Possibly add some borders and padding as it looks a little squached together. + +``` ++ gh +- jo + + test (main/next/dev) + - tasyn (main/next/dev) + * 12ab32f (dev) added feature X + * bce43b1 (next) added feature Y + * f43e379 (main) added feature Z + - git-next (main/next/dev) DEV NOT AHEAD OF MAIN - rebase onto main + * 239cefd (main/next) fix bug A + * b4c290a (dev) +``` + +Adding a border around open forges: + +``` ++ gh +- jo --------------------------------------------------------------------+ +| + test (main/next/dev) | +| - tasyn (main/next/dev) | +| * 12ab32f (dev) added feature X | +| * bce43b1 (next) added feature Y | +| * f43e379 (main) added feature Z | +| - git-next (main/next/dev) DEV NOT AHEAD OF MAIN - rebase onto main | +| * 239cefd (main/next) fix bug A | +| * b4c290a (dev) | ++------------------------------------------------------------------------+ +``` + +Adding a border around open forges and repos (I like this one the best): + +``` ++ gh +- jo --------------------------------------------------------------------+ +| + test (main/next/dev) | +| - tasyn (main/next/dev) ---------------------------------------------+ | +| | * 12ab32f (dev) added feature X | | +| | * bce43b1 (next) added feature Y | | +| | * f43e379 (main) added feature Z | | +| +--------------------------------------------------------------------+ | +| - git-next (main/next/dev) DEV NOT AHEAD OF MAIN - rebase onto main -+ | +| | * 239cefd (main/next) fix bug A | | +| | * b4c290a (dev) | | +| +--------------------------------------------------------------------+ | ++------------------------------------------------------------------------+ +``` + +## Logging + +- tui-logger to create an optional panel to show the normal server logs + +## Branch Graph + +- tui-nodes ? + +## Scrolling + +- tui-scrollview + +## Tree View + +- tui-tree-widget diff --git a/crates/cli/src/tui/actor/handlers/server_update.rs b/crates/cli/src/tui/actor/handlers/server_update.rs index d5c6bad..2254350 100644 --- a/crates/cli/src/tui/actor/handlers/server_update.rs +++ b/crates/cli/src/tui/actor/handlers/server_update.rs @@ -1,27 +1,91 @@ -use std::time::Instant; - +// use actix::Handler; -use crate::{server::actor::messages::ServerUpdate, tui::Tui}; +use crate::{ + server::actor::messages::{RepoUpdate, ServerUpdate}, + tui::{actor::ServerState, Tui}, +}; -// impl Handler for Tui { type Result = (); fn handle(&mut self, msg: ServerUpdate, _ctx: &mut Self::Context) -> Self::Result { + self.state.tap(); match msg { - ServerUpdate::UpdateRepoSummary { + ServerUpdate::AppConfigLoaded { app_config } => { + self.state.mode = ServerState::from(app_config); + } + + ServerUpdate::RepoUpdate { forge_alias, repo_alias, - branches, - log, - } => todo!(), - ServerUpdate::RemoveRepo { - forge_alias, - repo_alias, - } => todo!(), - ServerUpdate::Ping => { - self.last_ping = Instant::now(); + repo_update, + } => { + if let ServerState::Configured { forges } = &mut self.state.mode { + let Some(forge_state) = forges.get_mut(&forge_alias) else { + return; + }; + let Some(repo_state) = forge_state.repos.get_mut(&repo_alias) else { + return; + }; + match repo_update { + RepoUpdate::Branches { + branches, + histories, + } => { + repo_state.update_branches(branches, histories); + } + RepoUpdate::Log { log } => { + repo_state.update_log(log); + } + RepoUpdate::ValidateRepo => repo_state.update_message("polling..."), + RepoUpdate::Okay { main, next, dev } => { + repo_state.clear_alert(); + repo_state.update_message("okay"); + *repo_state = repo_state.clone().ready(main, next, dev); + } + RepoUpdate::Alert { alert } => { + repo_state.alert(alert); + } + RepoUpdate::CheckingCI => { + repo_state.update_message("Checking CI status"); + } + RepoUpdate::AdvancingNext { + commit: _, + force: _, + } => (), + RepoUpdate::AdvancingMain { commit } => { + repo_state.update_message(format!("advancing main to {commit}")); + } + RepoUpdate::Opening => { + repo_state.update_message("opening..."); + } + RepoUpdate::Opened => { + repo_state.update_message("opened"); + } + RepoUpdate::LoadingConfigFromRepo => { + repo_state.update_message("loading config from repo..."); + } + RepoUpdate::ReceiveCIStatus { status } => { + repo_state.update_message(format!("ci status: {status:?}")); + } + RepoUpdate::ReceiveRepoConfig { repo_config: _ } => { + repo_state.update_message("loaded config from repo"); + } + RepoUpdate::RegisteringWebhook => { + repo_state.update_message("registering webhook..."); + } + RepoUpdate::UnregisteringWebhook => { + repo_state.update_message("unregistering webhook..."); + } + RepoUpdate::WebhookReceived { branch, push: _ } => { + repo_state.update_message(format!("webhook update: {branch:?}")); + } + RepoUpdate::RegisteredWebhook => { + repo_state.update_message("registered webhook"); + } + } + } } } } diff --git a/crates/cli/src/tui/actor/handlers/tick.rs b/crates/cli/src/tui/actor/handlers/tick.rs index 215bf1f..44ecb83 100644 --- a/crates/cli/src/tui/actor/handlers/tick.rs +++ b/crates/cli/src/tui/actor/handlers/tick.rs @@ -1,12 +1,5 @@ // -use std::{borrow::BorrowMut, time::Instant}; - -use actix::{ActorContext, Handler}; -use ratatui::{ - crossterm::event::{self, KeyCode, KeyEventKind}, - style::Stylize as _, - widgets::Paragraph, -}; +use actix::Handler; use crate::tui::actor::{messages::Tick, Tui}; @@ -14,44 +7,9 @@ impl Handler for Tui { type Result = std::io::Result<()>; fn handle(&mut self, _msg: Tick, ctx: &mut Self::Context) -> Self::Result { - if let Some(terminal) = self.terminal.borrow_mut() { - terminal.draw(|frame| { - let area = frame.area(); - frame.render_widget( - Paragraph::new(format!( - "(press 'q' to quit) Ping:[{:?}] UI:[{:?}]", - self.last_ping, - Instant::now() - )) - .white() - .on_blue(), - area, - ); - })?; - } else { - eprintln!("No terminal setup"); - } - if event::poll(std::time::Duration::from_millis(16))? { - if let event::Event::Key(key) = event::read()? { - if key.kind == KeyEventKind::Press { - match key.code { - KeyCode::Char('q') => { - // execute!(stderr(), LeaveAlternateScreen)?; - // disable_raw_mode()?; - ctx.stop(); - if let Err(err) = self.signal_shutdown.send(()) { - tracing::error!(?err, "Failed to signal shutdown"); - } - } - KeyCode::Esc => { - // - } - _ => (), - } - } - } - } - + self.state.tap(); + self.draw()?; + self.handle_input(ctx)?; Ok(()) } } diff --git a/crates/cli/src/tui/actor/messages.rs b/crates/cli/src/tui/actor/messages.rs index 3af3fc1..b47cbc8 100644 --- a/crates/cli/src/tui/actor/messages.rs +++ b/crates/cli/src/tui/actor/messages.rs @@ -1,4 +1,4 @@ // use git_next_core::message; -message!(Tick => std::io::Result<()>, "Start the TUI"); +message!(Tick => std::io::Result<()>, "Update the TUI"); diff --git a/crates/cli/src/tui/actor/mod.rs b/crates/cli/src/tui/actor/mod.rs index b00b4a3..855b43c 100644 --- a/crates/cli/src/tui/actor/mod.rs +++ b/crates/cli/src/tui/actor/mod.rs @@ -1,56 +1,33 @@ // mod handlers; pub mod messages; +mod model; -use std::{ - io::{stderr, Stderr}, - sync::mpsc::Sender, - time::Instant, -}; +use std::sync::mpsc::Sender; -use actix::{Actor, Context}; +use actix::{Actor, ActorContext as _, Context}; + +pub use model::*; use ratatui::{ - crossterm::{ - execute, - terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen}, - }, - prelude::CrosstermBackend, - Terminal, + crossterm::event::{self, KeyCode, KeyEventKind}, + DefaultTerminal, }; #[derive(Debug)] pub struct Tui { - terminal: Option>>, + terminal: Option, signal_shutdown: Sender<()>, - last_ping: Instant, + pub state: State, } impl Actor for Tui { type Context = Context; fn started(&mut self, _ctx: &mut Self::Context) { - match init() { - Err(err) => { - eprintln!("Failed to enable raw mode: {err:?}"); - } - Ok(terminal) => { - self.terminal.replace(terminal); - } - } + self.terminal.replace(ratatui::init()); } fn stopped(&mut self, _ctx: &mut Self::Context) { - if let Err(err) = restore() { - match std::env::consts::OS { - "linux" | "macos" => { - eprintln!( - "Failed to restore terminal: Type `reset` to restore terminal: {err:?}" - ); - } - "windows" => { - println!("Failed to restore terminal: Reopen a new terminal: {err:?}"); - } - _ => println!("Failed to restore terminal: {err:?}"), - } - } + self.terminal.take(); + ratatui::restore(); } } impl Tui { @@ -58,18 +35,46 @@ impl Tui { Self { terminal: None, signal_shutdown, - last_ping: Instant::now(), + state: State::initial(), } } -} -fn init() -> std::io::Result>> { - execute!(stderr(), EnterAlternateScreen)?; - enable_raw_mode()?; - Terminal::new(CrosstermBackend::new(stderr())) -} + pub const fn state(&self) -> &State { + &self.state + } -fn restore() -> std::io::Result<()> { - execute!(stderr(), LeaveAlternateScreen)?; - disable_raw_mode() + fn draw(&mut self) -> std::io::Result<()> { + let t = self.terminal.take(); + if let Some(mut terminal) = t { + terminal.draw(|frame| { + frame.render_widget(self.state(), frame.area()); + })?; + self.terminal = Some(terminal); + } else { + eprintln!("No terminal setup"); + } + Ok(()) + } + + fn handle_input(&self, ctx: &mut ::Context) -> std::io::Result<()> { + if event::poll(std::time::Duration::from_millis(16))? { + if let event::Event::Key(key) = event::read()? { + if key.kind == KeyEventKind::Press { + match key.code { + KeyCode::Char('q') => { + ctx.stop(); + if let Err(err) = self.signal_shutdown.send(()) { + tracing::error!(?err, "Failed to signal shutdown"); + } + } + KeyCode::Esc => { + // + } + _ => (), + } + } + } + } + Ok(()) + } } diff --git a/crates/cli/src/tui/actor/model.rs b/crates/cli/src/tui/actor/model.rs new file mode 100644 index 0000000..91d6c76 --- /dev/null +++ b/crates/cli/src/tui/actor/model.rs @@ -0,0 +1,380 @@ +// +use ratatui::{ + layout::Alignment, + prelude::{Buffer, Rect}, + style::Stylize as _, + symbols::border, + text::Line, + widgets::{block::Title, Block, Paragraph, Widget}, +}; + +use git_next_core::{ + git::{self, graph::Log, Commit}, + ForgeAlias, RepoAlias, RepoBranches, +}; +use tracing::info; + +use std::{collections::BTreeMap, fmt::Display, time::Instant}; + +use crate::{server::actor::messages::ValidAppConfig, tui::components::ConfiguredAppWidget}; + +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct State { + last_update: Instant, + started: Instant, + pub mode: ServerState, +} +impl State { + pub fn initial() -> Self { + Self { + last_update: Instant::now(), + started: Instant::now(), + mode: ServerState::Initial { tick: 0 }, + } + } + + pub fn tap(&mut self) { + self.last_update = Instant::now(); + if let ServerState::Initial { tick } = &mut self.mode { + *tick += 1; + } + } +} + +#[derive(Clone, Debug, PartialEq, Eq)] +pub enum ServerState { + /// UI has started but has no information on the state of the server + Initial { tick: usize }, // NOTE: for use with throbber-widgets-tui ? + + /// The application configuration has been loaded, individual forges and repos have their own + /// states + Configured { + forges: BTreeMap, + }, +} +impl ServerState { + pub fn update_branches( + &mut self, + forge_alias: &ForgeAlias, + repo_alias: &RepoAlias, + branches: RepoBranches, + ) { + if let Self::Configured { forges } = self { + let Some(forge_state) = forges.get_mut(forge_alias) else { + return; + }; + let Some(repo_state) = forge_state.repos.get_mut(repo_alias) else { + return; + }; + match repo_state { + RepoState::Configured { + branches: state_branches, + .. + } + | RepoState::Ready { + branches: state_branches, + .. + } => *state_branches = branches, + + RepoState::Identified { .. } => (), + } + } + } + + pub fn update_log(&mut self, forge_alias: &ForgeAlias, repo_alias: &RepoAlias, log: Log) { + if let Self::Configured { forges } = self { + let Some(forge_state) = forges.get_mut(forge_alias) else { + return; + }; + let Some(repo_state) = forge_state.repos.get_mut(repo_alias) else { + return; + }; + match repo_state { + RepoState::Ready { log: state_log, .. } => *state_log = log, + + RepoState::Identified { .. } | RepoState::Configured { .. } => (), + } + } + } +} +impl From for ServerState { + fn from(app_config: ValidAppConfig) -> Self { + Self::Configured { + forges: app_config + .app_config + .forges() + .map(|(forge_alias, config)| { + ( + forge_alias, + config + .repos() + .map(|(repo_alias, server_repo_config)| { + (repo_alias, server_repo_config.repo_config()) + }) + .map( + |(repo_alias, option_repo_config)| match option_repo_config { + Some(rc) => ( + repo_alias.clone(), + RepoState::Configured { + repo_alias, + message: "configured".into(), + messages: Vec::new(), + alert: None, + branches: rc.branches().clone(), + histories: None, + log: git::graph::Log::default(), + }, + ), + None => ( + repo_alias.clone(), + RepoState::Identified { + repo_alias, + message: "identified".into(), + messages: Vec::new(), + alert: None, + }, + ), + }, + ) + .collect::>(), + ) + }) + .map(|(forge_alias, vec_repo_alias_state)| { + let forge_state: ForgeState = ForgeState { + alias: forge_alias.clone(), + view_state: ViewState::default(), + repos: vec_repo_alias_state.into_iter().collect::>(), + }; + (forge_alias, forge_state) + }) + .collect::>(), + } + } +} + +#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)] +pub enum ViewState { + Collapsed, + #[default] + Expanded, +} +impl Display for ViewState { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let view_state = match self { + Self::Collapsed => "+", + Self::Expanded => "-", + }; + write!(f, "{view_state}") + } +} + +#[derive(Clone, Debug, Default, PartialEq, Eq)] +pub struct ForgeState { + pub alias: ForgeAlias, + pub view_state: ViewState, + pub repos: BTreeMap, +} + +#[derive(Clone, Debug, PartialEq, Eq)] +pub enum RepoState { + Identified { + repo_alias: RepoAlias, + message: String, + messages: Vec, + alert: Option, + }, + Configured { + repo_alias: RepoAlias, + message: String, + messages: Vec, + alert: Option, + branches: RepoBranches, + histories: Option, + log: Log, + }, + Ready { + repo_alias: RepoAlias, + message: String, + messages: Vec, + alert: Option, + branches: RepoBranches, + histories: Option, + view_state: ViewState, + main: Commit, + next: Commit, + dev: Commit, + log: Log, + }, +} +impl RepoState { + #[tracing::instrument] + pub fn update_branches( + &mut self, + branches: RepoBranches, + histories: Option, + ) { + match self { + Self::Configured { + branches: state_branches, + histories: state_histories, + .. + } + | Self::Ready { + branches: state_branches, + histories: state_histories, + .. + } => { + *state_branches = branches; + *state_histories = histories; + } + + Self::Identified { .. } => (), + } + } + + #[tracing::instrument] + pub fn update_log(&mut self, log: Log) { + match self { + Self::Configured { log: state_log, .. } | Self::Ready { log: state_log, .. } => { + *state_log = log; + } + + Self::Identified { .. } => { + info!("git graph log ignored by ui"); + } + } + } + + #[tracing::instrument] + pub fn update_message(&mut self, msg: impl Into + std::fmt::Debug) { + tracing::info!("new tui message"); + let msg: String = msg.into(); + match self { + Self::Identified { + message, messages, .. + } + | Self::Configured { + message, messages, .. + } + | Self::Ready { + message, messages, .. + } => { + messages.push(msg.clone()); + *message = msg; + } + } + } + + #[tracing::instrument] + pub fn clear_alert(&mut self) { + match self { + Self::Identified { .. } | Self::Configured { .. } => (), + Self::Ready { alert, .. } => *alert = None, + } + } + + #[tracing::instrument] + pub fn alert(&mut self, msg: impl Into + std::fmt::Debug) { + let msg: String = msg.into(); + tracing::info!(%msg, "new tui alert"); + self.update_message("ALERT"); + match self { + Self::Identified { alert, .. } + | Self::Configured { alert, .. } + | Self::Ready { alert, .. } => *alert = Some(msg), + } + } + + pub fn ready(self, main: Commit, next: Commit, dev: Commit) -> Self { + match self { + Self::Identified { + repo_alias, + message, + messages, + alert, + } => Self::Identified { + repo_alias, + message, + messages, + alert, + }, + Self::Configured { + repo_alias, + message, + messages, + alert, + branches, + histories, + log, + } => Self::Ready { + repo_alias, + message, + messages, + alert, + branches, + histories, + view_state: ViewState::Expanded, + main, + next, + dev, + log, + }, + Self::Ready { + repo_alias, + message, + messages, + alert, + branches, + histories, + view_state, + log, + .. // drop existing main, next and dev to use parameters + } => Self::Ready { + repo_alias, + message, + messages, + alert, + branches, + histories, + view_state, + main, + next, + dev, + log, + }, + } + } +} + +impl Widget for &State { + fn render(self, area: Rect, buf: &mut Buffer) + where + Self: Sized, + { + let block = Block::bordered() + .title(Title::from(" Git-Next ".bold()).alignment(Alignment::Center)) + .title( + Title::from(Line::from(vec![ + " [q]uit ".into(), + format!( + "{}s ", + self.last_update.duration_since(self.started).as_secs() + ) + .into(), + ])) + .alignment(Alignment::Center) + .position(ratatui::widgets::block::Position::Bottom), + ) + .border_set(border::THICK); + let interior = block.inner(area); + block.render(area, buf); + match &self.mode { + ServerState::Initial { tick } => Paragraph::new(format!("Loading...{tick}")) + .centered() + .render(interior, buf), + ServerState::Configured { forges } => { + ConfiguredAppWidget { forges }.render(interior, buf); + } + } + } +} diff --git a/crates/cli/src/tui/components/configured_app.rs b/crates/cli/src/tui/components/configured_app.rs new file mode 100644 index 0000000..cc9ad04 --- /dev/null +++ b/crates/cli/src/tui/components/configured_app.rs @@ -0,0 +1,52 @@ +use std::collections::BTreeMap; + +use git_next_core::ForgeAlias; +use ratatui::{ + buffer::Buffer, + layout::{Constraint, Direction, Layout, Rect}, + text::Line, + widgets::Widget, +}; + +use crate::tui::actor::{ForgeState, ViewState}; + +use super::forge::ForgeWidget; + +pub struct ConfiguredAppWidget<'a> { + pub forges: &'a BTreeMap, +} +impl<'a> Widget for ConfiguredAppWidget<'a> { + fn render(self, area: Rect, buf: &mut Buffer) + where + Self: Sized, + { + let layout_app = Layout::default() + .direction(Direction::Vertical) + .constraints(vec![Constraint::Length(1), Constraint::Fill(1)]) + .split(area); + + Line::from(format!("Forges: ({})", self.forges.keys().len())).render(layout_app[0], buf); + + let layout_forge_list = Layout::default() + .direction(Direction::Vertical) + .constraints( + self.forges + .iter() + .map(|(_alias, state)| match state.view_state { + ViewState::Collapsed => Constraint::Length(1), + ViewState::Expanded => Constraint::Fill(1), + }), + ) + .split(layout_app[1]); + + self.forges + .iter() + .map(|(forge_alias, state)| ForgeWidget { + forge_alias, + repos: &state.repos, + view_state: state.view_state, + }) + .enumerate() + .for_each(|(i, w)| w.render(layout_forge_list[i], buf)); + } +} diff --git a/crates/cli/src/tui/components/forge/collapsed.rs b/crates/cli/src/tui/components/forge/collapsed.rs new file mode 100644 index 0000000..72761e5 --- /dev/null +++ b/crates/cli/src/tui/components/forge/collapsed.rs @@ -0,0 +1,15 @@ +// +use git_next_core::ForgeAlias; +use ratatui::{buffer::Buffer, layout::Rect, text::Text, widgets::Widget}; + +pub struct CollapsedForgeWidget<'a> { + pub forge_alias: &'a ForgeAlias, +} +impl<'a> Widget for CollapsedForgeWidget<'a> { + fn render(self, area: Rect, buf: &mut Buffer) + where + Self: Sized, + { + Text::from(format!("- {}", self.forge_alias)).render(area, buf); + } +} diff --git a/crates/cli/src/tui/components/forge/expanded.rs b/crates/cli/src/tui/components/forge/expanded.rs new file mode 100644 index 0000000..9d6a414 --- /dev/null +++ b/crates/cli/src/tui/components/forge/expanded.rs @@ -0,0 +1,40 @@ +// +use std::collections::BTreeMap; + +use git_next_core::{ForgeAlias, RepoAlias}; +use ratatui::{ + buffer::Buffer, + layout::{Alignment, Constraint, Direction, Layout, Rect}, + widgets::{block::Title, Block, Borders, Widget}, +}; + +use crate::tui::{actor::RepoState, components::repo::RepoWidget}; + +pub struct ExpandedForgeWidget<'a> { + pub forge_alias: &'a ForgeAlias, + pub repos: &'a BTreeMap, +} +impl<'a> Widget for ExpandedForgeWidget<'a> { + fn render(self, area: Rect, buf: &mut Buffer) + where + Self: Sized, + { + let block = Block::default() + .title(Title::from(self.forge_alias.to_string()).alignment(Alignment::Center)) + .borders(Borders::ALL); + let layout = Layout::default() + .direction(Direction::Vertical) + .constraints( + self.repos + .iter() + .map(|(_alias, _state)| Constraint::Fill(1)), + ) + .split(block.inner(area)); + block.render(area, buf); + self.repos + .values() + .map(|repo_state| RepoWidget { repo_state }) + .enumerate() + .for_each(|(i, w)| w.render(layout[i], buf)); + } +} diff --git a/crates/cli/src/tui/components/forge/mod.rs b/crates/cli/src/tui/components/forge/mod.rs new file mode 100644 index 0000000..3851d78 --- /dev/null +++ b/crates/cli/src/tui/components/forge/mod.rs @@ -0,0 +1,34 @@ +// +mod collapsed; +mod expanded; + +use std::collections::BTreeMap; + +use git_next_core::{ForgeAlias, RepoAlias}; +use ratatui::{buffer::Buffer, layout::Rect, widgets::Widget}; + +use crate::tui::actor::{RepoState, ViewState}; + +pub struct ForgeWidget<'a> { + pub forge_alias: &'a ForgeAlias, + pub repos: &'a BTreeMap, + pub view_state: ViewState, +} +impl<'a> Widget for ForgeWidget<'a> { + fn render(self, area: Rect, buf: &mut Buffer) + where + Self: Sized, + { + match self.view_state { + ViewState::Collapsed => collapsed::CollapsedForgeWidget { + forge_alias: self.forge_alias, + } + .render(area, buf), + ViewState::Expanded => expanded::ExpandedForgeWidget { + forge_alias: self.forge_alias, + repos: self.repos, + } + .render(area, buf), + } + } +} diff --git a/crates/cli/src/tui/components/history.rs b/crates/cli/src/tui/components/history.rs new file mode 100644 index 0000000..32714e5 --- /dev/null +++ b/crates/cli/src/tui/components/history.rs @@ -0,0 +1,25 @@ +use git_next_core::git::graph::Log; +// +use ratatui::{ + text::{Line, Text}, + widgets::{Paragraph, Widget}, +}; + +pub struct CommitLog<'a> { + pub log: &'a Log, +} +impl<'a> Widget for CommitLog<'a> { + fn render(self, area: ratatui::prelude::Rect, buf: &mut ratatui::prelude::Buffer) + where + Self: Sized, + { + Paragraph::new(Text::from( + self.log + .iter() + .map(ToString::to_string) + .map(Line::from) + .collect::>(), + )) + .render(area, buf); + } +} diff --git a/crates/cli/src/tui/components/mod.rs b/crates/cli/src/tui/components/mod.rs new file mode 100644 index 0000000..30414f0 --- /dev/null +++ b/crates/cli/src/tui/components/mod.rs @@ -0,0 +1,8 @@ +// +mod configured_app; +mod forge; +mod history; +mod repo; + +pub use configured_app::ConfiguredAppWidget; +pub use history::CommitLog; diff --git a/crates/cli/src/tui/components/repo/configured.rs b/crates/cli/src/tui/components/repo/configured.rs new file mode 100644 index 0000000..ae342ce --- /dev/null +++ b/crates/cli/src/tui/components/repo/configured.rs @@ -0,0 +1,43 @@ +// +use git_next_core::{git, RepoAlias, RepoBranches}; + +use ratatui::{ + buffer::Buffer, + layout::{Constraint, Direction, Layout, Rect}, + widgets::{Block, Borders, Widget}, +}; + +use crate::tui::components::CommitLog; + +use super::{identity::Identity, messages::Messages}; + +pub struct ConfiguredRepoWidget<'a> { + pub repo_alias: &'a RepoAlias, + pub message: &'a str, + pub messages: &'a Vec, + pub alert: Option<&'a str>, + pub branches: &'a RepoBranches, + pub log: &'a git::graph::Log, +} +impl<'a> Widget for ConfiguredRepoWidget<'a> { + fn render(self, area: Rect, buf: &mut Buffer) + where + Self: Sized, + { + let block = Block::default() + .title(Identity::new( + self.repo_alias, + self.alert, + self.message, + Some(self.branches), + )) + .borders(Borders::ALL); + let layout = Layout::default() + .direction(Direction::Vertical) + .constraints(vec![Constraint::Fill(1), Constraint::Fill(1)]) + .split(block.inner(area)); + block.render(area, buf); + Messages::new(self.messages).render(layout[0], buf); + CommitLog { log: self.log }.render(layout[1], buf); + } +} diff --git a/crates/cli/src/tui/components/repo/identified.rs b/crates/cli/src/tui/components/repo/identified.rs new file mode 100644 index 0000000..53eefb8 --- /dev/null +++ b/crates/cli/src/tui/components/repo/identified.rs @@ -0,0 +1,34 @@ +// +use git_next_core::RepoAlias; + +use ratatui::{ + buffer::Buffer, + layout::Rect, + widgets::{Block, Borders, Widget}, +}; + +use super::{identity::Identity, messages::Messages}; + +pub struct IdentifiedRepoWidget<'a> { + pub repo_alias: &'a RepoAlias, + pub message: &'a str, + pub messages: &'a Vec, + pub alert: Option<&'a str>, +} +impl<'a> Widget for IdentifiedRepoWidget<'a> { + fn render(self, area: Rect, buf: &mut Buffer) + where + Self: Sized, + { + let block = Block::default() + .title(Identity::new( + self.repo_alias, + self.alert, + self.message, + None, + )) + .borders(Borders::ALL); + Messages::new(self.messages).render(block.inner(area), buf); + block.render(area, buf); + } +} diff --git a/crates/cli/src/tui/components/repo/identity.rs b/crates/cli/src/tui/components/repo/identity.rs new file mode 100644 index 0000000..28e0917 --- /dev/null +++ b/crates/cli/src/tui/components/repo/identity.rs @@ -0,0 +1,61 @@ +// +use std::fmt::Display; + +use git_next_core::{RepoAlias, RepoBranches}; +use ratatui::{ + layout::Alignment, + text::Text, + widgets::{block::Title, Widget}, +}; + +pub struct Identity<'a> { + pub repo_alias: &'a RepoAlias, + pub alert: Option<&'a str>, + pub message: &'a str, + pub repo_branches: Option<&'a RepoBranches>, +} +impl<'a> Identity<'a> { + pub const fn new( + repo_alias: &'a RepoAlias, + alert: Option<&'a str>, + message: &'a str, + repo_branches: Option<&'a RepoBranches>, + ) -> Self { + Self { + repo_alias, + alert, + message, + repo_branches, + } + } +} +impl<'a> Widget for Identity<'a> { + fn render(self, area: ratatui::prelude::Rect, buf: &mut ratatui::prelude::Buffer) + where + Self: Sized, + { + Text::from(self.to_string()).render(area, buf); + } +} +impl<'a> Display for Identity<'a> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let repo_alias = self.repo_alias; + let alert = self.alert.unwrap_or(""); + let message = self.message; + let text = self.repo_branches.map_or_else( + || format!("{repo_alias} {alert} (_/_/_) [{message}]"), + |branches| { + let main = branches.main(); + let next = branches.next(); + let dev = branches.dev(); + format!("{repo_alias} {alert} ({main}/{next}/{dev}) [{message}]") + }, + ); + f.write_str(text.as_str()) + } +} +impl<'a> From> for Title<'a> { + fn from(identity: Identity<'a>) -> Self { + Self::from(identity.to_string()).alignment(Alignment::Left) + } +} diff --git a/crates/cli/src/tui/components/repo/messages.rs b/crates/cli/src/tui/components/repo/messages.rs new file mode 100644 index 0000000..38fa8d5 --- /dev/null +++ b/crates/cli/src/tui/components/repo/messages.rs @@ -0,0 +1,26 @@ +// +use ratatui::{ + text::{Line, Text}, + widgets::Widget, +}; + +pub struct Messages<'a>(&'a Vec); +impl<'a> Messages<'a> { + pub const fn new(messages: &'a Vec) -> Self { + Self(messages) + } +} +impl<'a> Widget for Messages<'a> { + fn render(self, area: ratatui::prelude::Rect, buf: &mut ratatui::prelude::Buffer) + where + Self: Sized, + { + Text::from( + self.0 + .iter() + .map(|m| Line::from(format!("- {m}"))) + .collect::>(), + ) + .render(area, buf); + } +} diff --git a/crates/cli/src/tui/components/repo/mod.rs b/crates/cli/src/tui/components/repo/mod.rs new file mode 100644 index 0000000..fa7a8e6 --- /dev/null +++ b/crates/cli/src/tui/components/repo/mod.rs @@ -0,0 +1,86 @@ +// +mod configured; +mod identified; +mod identity; +mod messages; +mod ready; + +use std::string::String; + +use configured::ConfiguredRepoWidget; +use identified::IdentifiedRepoWidget; +use ratatui::{buffer::Buffer, layout::Rect, widgets::Widget}; +use ready::ReadyRepoWidget; + +use crate::tui::actor::RepoState; + +pub struct RepoWidget<'a> { + pub repo_state: &'a RepoState, +} +impl<'a> Widget for RepoWidget<'a> { + fn render(self, area: Rect, buf: &mut Buffer) + where + Self: Sized, + { + match self.repo_state { + RepoState::Identified { + repo_alias, + message, + messages, + alert, + } => { + IdentifiedRepoWidget { + repo_alias, + message, + messages, + alert: alert.as_ref().map(String::as_str), + } + .render(area, buf); + } + + RepoState::Configured { + repo_alias, + message, + messages, + alert, + branches, + log, + .. + } => ConfiguredRepoWidget { + repo_alias, + message, + messages, + alert: alert.as_ref().map(String::as_str), + branches, + log, + } + .render(area, buf), + + RepoState::Ready { + repo_alias, + message, + messages, + alert, + branches, + log, + // view_state, + // main, + // next, + // dev, + .. + } => ReadyRepoWidget { + repo_alias, + message, + messages, + alert: alert.as_ref().map(String::as_str), + branches, + log, + // view_state, + // main, + // next, + // dev, + } + .render(area, buf), + }; + } +} diff --git a/crates/cli/src/tui/components/repo/ready.rs b/crates/cli/src/tui/components/repo/ready.rs new file mode 100644 index 0000000..d1349c7 --- /dev/null +++ b/crates/cli/src/tui/components/repo/ready.rs @@ -0,0 +1,49 @@ +// +use git_next_core::{RepoAlias, RepoBranches}; + +use ratatui::{ + buffer::Buffer, + layout::{Constraint, Direction, Layout, Rect}, + widgets::{Block, Borders, Widget}, +}; + +use crate::{git, tui::components::CommitLog}; + +use super::{identity::Identity, messages::Messages}; + +pub struct ReadyRepoWidget<'a> { + pub repo_alias: &'a RepoAlias, + pub message: &'a str, + pub messages: &'a Vec, + pub alert: Option<&'a str>, + pub branches: &'a RepoBranches, + pub log: &'a git::graph::Log, + // pub view_state: &'a actor::ViewState, + // pub main: &'a Commit, + // pub next: &'a Commit, + // pub dev: &'a Commit, +} +impl<'a> Widget for ReadyRepoWidget<'a> { + fn render(self, area: Rect, buf: &mut Buffer) + where + Self: Sized, + { + let block = Block::default() + .title(Identity::new( + self.repo_alias, + self.alert, + self.message, + Some(self.branches), + )) + .borders(Borders::ALL); + let layout_repo = Layout::default() + .direction(Direction::Vertical) + .constraints(vec![Constraint::Fill(1), Constraint::Fill(1)]) + .split(block.inner(area)); + block.render(area, buf); + + Messages::new(self.messages).render(layout_repo[0], buf); + + CommitLog { log: self.log }.render(layout_repo[1], buf); + } +} diff --git a/crates/cli/src/tui/logging.rs b/crates/cli/src/tui/logging.rs new file mode 100644 index 0000000..954a501 --- /dev/null +++ b/crates/cli/src/tui/logging.rs @@ -0,0 +1,86 @@ +// +use std::path::PathBuf; + +use color_eyre::eyre::Result; +use directories::ProjectDirs; +use lazy_static::lazy_static; +use tracing_error::ErrorLayer; +use tracing_subscriber::{self, layer::SubscriberExt, util::SubscriberInitExt, Layer}; + +lazy_static! { + pub static ref PROJECT_NAME: String = env!("CARGO_CRATE_NAME").to_uppercase(); + pub static ref DATA_FOLDER: Option = + std::env::var(format!("{}_DATA", PROJECT_NAME.clone())) + .ok() + .map(PathBuf::from); + pub static ref LOG_ENV: String = format!("{}_LOGLEVEL", PROJECT_NAME.clone()); + pub static ref LOG_FILE: String = format!("{}.log", env!("CARGO_PKG_NAME")); +} + +fn project_directory() -> Option { + ProjectDirs::from("net", "kemitix", env!("CARGO_PKG_NAME")) +} + +pub fn get_data_dir() -> PathBuf { + let directory = DATA_FOLDER.clone().map_or_else( + || { + project_directory().map_or_else( + || PathBuf::from(".").join(".data"), + |proj_dirs| proj_dirs.data_local_dir().to_path_buf(), + ) + }, + |data_folder| data_folder, + ); + directory +} + +pub fn initialize_logging() -> Result<()> { + let directory = get_data_dir(); + std::fs::create_dir_all(directory.clone())?; + let log_path = directory.join(LOG_FILE.clone()); + let log_file = std::fs::File::create(log_path)?; + std::env::set_var( + "RUST_LOG", + std::env::var("RUST_LOG") + .or_else(|_| std::env::var(LOG_ENV.clone())) + .unwrap_or_else(|_| format!("{}=info", env!("CARGO_CRATE_NAME"))), + ); + let file_subscriber = tracing_subscriber::fmt::layer() + .with_file(true) + .with_line_number(true) + .with_writer(log_file) + .with_target(false) + .with_ansi(false) + .with_filter(tracing_subscriber::filter::EnvFilter::from_default_env()); + tracing_subscriber::registry() + .with(file_subscriber) + .with(ErrorLayer::default()) + .init(); + Ok(()) +} + +/// Similar to the `std::dbg!` macro, but generates `tracing` events rather +/// than printing to stdout. +/// +/// By default, the verbosity level for the generated events is `DEBUG`, but +/// this can be customized. +#[macro_export] +macro_rules! trace_dbg { + (target: $target:expr, level: $level:expr, $ex:expr) => {{ + match $ex { + value => { + tracing::event!(target: $target, $level, ?value, stringify!($ex)); + value + } + } + }}; + (level: $level:expr, $ex:expr) => { + trace_dbg!(target: module_path!(), level: $level, $ex) + }; + (target: $target:expr, $ex:expr) => { + trace_dbg!(target: $target, level: tracing::Level::DEBUG, $ex) + }; + ($ex:expr) => { + trace_dbg!(level: tracing::Level::DEBUG, $ex) + }; +} diff --git a/crates/cli/src/tui/mod.rs b/crates/cli/src/tui/mod.rs index 508e8de..b77d3ad 100644 --- a/crates/cli/src/tui/mod.rs +++ b/crates/cli/src/tui/mod.rs @@ -1,5 +1,7 @@ // mod actor; +pub mod components; +pub mod logging; pub use actor::messages::Tick; pub use actor::Tui; diff --git a/crates/core/src/config/server_repo_config.rs b/crates/core/src/config/server_repo_config.rs index 44f4456..83013eb 100644 --- a/crates/core/src/config/server_repo_config.rs +++ b/crates/core/src/config/server_repo_config.rs @@ -47,7 +47,8 @@ impl ServerRepoConfig { } /// Returns a `RepoConfig` from the server configuration if ALL THREE branches were provided - pub(crate) fn repo_config(&self) -> Option { + #[must_use] + pub fn repo_config(&self) -> Option { match (&self.main, &self.next, &self.dev) { (Some(main), Some(next), Some(dev)) => Some(RepoConfig::new( RepoBranches::new(main.to_string(), next.to_string(), dev.to_string()), diff --git a/crates/core/src/config/webhook/push.rs b/crates/core/src/config/webhook/push.rs index 76c515f..6fd051d 100644 --- a/crates/core/src/config/webhook/push.rs +++ b/crates/core/src/config/webhook/push.rs @@ -2,7 +2,7 @@ use crate::config::{BranchName, RepoBranches}; use derive_more::Constructor; -#[derive(Debug, Constructor, derive_with::With)] +#[derive(Clone, Debug, Constructor, PartialEq, Eq, derive_with::With)] pub struct Push { branch: BranchName, sha: String, @@ -34,7 +34,7 @@ impl Push { } } -#[derive(Debug, PartialEq, Eq)] +#[derive(Clone, Debug, PartialEq, Eq)] pub enum Branch { Main, Next, diff --git a/crates/core/src/git/commit.rs b/crates/core/src/git/commit.rs index 99b821d..792100f 100644 --- a/crates/core/src/git/commit.rs +++ b/crates/core/src/git/commit.rs @@ -62,7 +62,7 @@ newtype!( "The commit message for a git commit." ); -#[derive(Clone, Debug)] +#[derive(Clone, Debug, PartialEq, Eq)] pub struct Histories { pub main: Vec, pub next: Vec, diff --git a/crates/core/src/git/graph.rs b/crates/core/src/git/graph.rs index 718c0da..5c6c5f7 100644 --- a/crates/core/src/git/graph.rs +++ b/crates/core/src/git/graph.rs @@ -1,5 +1,4 @@ // - use std::borrow::ToOwned; use take_until::TakeUntilExt; diff --git a/crates/core/src/git/push.rs b/crates/core/src/git/push.rs index a791923..25be1b4 100644 --- a/crates/core/src/git/push.rs +++ b/crates/core/src/git/push.rs @@ -1,7 +1,7 @@ // use crate::{git, git::repository::open::OpenRepositoryLike, BranchName}; -#[derive(Debug, PartialEq, Eq)] +#[derive(Clone, Debug, PartialEq, Eq)] pub enum Force { No, From(git::GitRef), diff --git a/crates/core/src/git/repository/open/mod.rs b/crates/core/src/git/repository/open/mod.rs index 1d8d3ed..74f25b0 100644 --- a/crates/core/src/git/repository/open/mod.rs +++ b/crates/core/src/git/repository/open/mod.rs @@ -94,7 +94,8 @@ pub trait OpenRepositoryLike: std::fmt::Debug + Sync { /// /// # Errors /// - /// Will return `Err` if there are any network connectivity issues with the remote server. + /// Will return `Err` if there are any problems with the branch name being invalid, or any + /// corruption of the git repository. fn commit_log( &self, branch_name: &BranchName, diff --git a/crates/core/src/git/user_notification.rs b/crates/core/src/git/user_notification.rs index 00692f3..828aea3 100644 --- a/crates/core/src/git/user_notification.rs +++ b/crates/core/src/git/user_notification.rs @@ -1,3 +1,5 @@ +use std::fmt::Display; + // use crate::{git::Commit, BranchName, ForgeAlias, RepoAlias}; use serde_json::json; @@ -115,3 +117,18 @@ impl UserNotification { } } } +impl Display for UserNotification { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let message = match self { + Self::CICheckFailed { commit, .. } => format!("CI Check Failed [{commit}]"), + Self::RepoConfigLoadFailure { reason, .. } => { + format!("Failed to load repo config: {reason}") + } + Self::WebhookRegistration { reason, .. } => { + format!("Failed to register webhook: {reason}") + } + Self::DevNotBasedOnMain { .. } => "Dev not based on main".to_string(), + }; + write!(f, "{message}") + } +} diff --git a/crates/core/src/git/validation/positions.rs b/crates/core/src/git/validation/positions.rs index 18d81c0..1c1e892 100644 --- a/crates/core/src/git/validation/positions.rs +++ b/crates/core/src/git/validation/positions.rs @@ -1,6 +1,6 @@ // use crate::{ - git::{self, graph::log, repository::open::OpenRepositoryLike, RepoDetails, UserNotification}, + git::{self, repository::open::OpenRepositoryLike, RepoDetails, UserNotification}, BranchName, RepoConfig, }; @@ -27,6 +27,7 @@ pub fn validate( open_repository: &dyn OpenRepositoryLike, repo_details: &git::RepoDetails, repo_config: &RepoConfig, + log: git::graph::Log, ) -> Result { let main_branch = repo_config.branches().main(); let next_branch = repo_config.branches().next(); @@ -61,7 +62,7 @@ pub fn validate( main_branch, dev_commit: dev, main_commit: main, - log: log(repo_details), + log, }, )); } @@ -129,7 +130,13 @@ fn is_based_on(commits: &[git::commit::Commit], needle: &git::Commit) -> bool { commits.iter().any(|commit| commit == needle) } -fn get_commit_histories( +/// Returns the commit logs for the main, next and dev branches +/// +/// # Errors +/// +/// Will return `Err` if there are any problems with the branch names being invalid, or any +/// corruption of the git repository. +pub fn get_commit_histories( open_repository: &dyn OpenRepositoryLike, repo_config: &RepoConfig, ) -> git::commit::log::Result { diff --git a/crates/core/src/git/validation/tests.rs b/crates/core/src/git/validation/tests.rs index 62f2820..0b27a79 100644 --- a/crates/core/src/git/validation/tests.rs +++ b/crates/core/src/git/validation/tests.rs @@ -75,7 +75,12 @@ mod positions { ); let repo_config = given::a_repo_config(); - let result = validate(&*repository, &repo_details, &repo_config); + let result = validate( + &*repository, + &repo_details, + &repo_config, + git::graph::Log::default(), + ); println!("{result:?}"); let_assert!(Err(err) = result, "validate"); @@ -115,7 +120,12 @@ mod positions { "open repo" ); - let result = validate(&*open_repository, &repo_details, &repo_config); + let result = validate( + &*open_repository, + &repo_details, + &repo_config, + git::graph::Log::default(), + ); println!("{result:?}"); assert!(matches!( @@ -154,7 +164,12 @@ mod positions { "open repo" ); - let result = validate(&*open_repository, &repo_details, &repo_config); + let result = validate( + &*open_repository, + &repo_details, + &repo_config, + git::graph::Log::default(), + ); println!("{result:?}"); assert!(matches!( @@ -193,7 +208,12 @@ mod positions { "open repo" ); - let result = validate(&*open_repository, &repo_details, &repo_config); + let result = validate( + &*open_repository, + &repo_details, + &repo_config, + git::graph::Log::default(), + ); println!("{result:?}"); assert!(matches!( @@ -240,7 +260,12 @@ mod positions { //when let_assert!( - Err(err) = validate(&*open_repository, &repo_details, &repo_config), + Err(err) = validate( + &*open_repository, + &repo_details, + &repo_config, + git::graph::Log::default() + ), "validate" ); @@ -325,7 +350,12 @@ mod positions { //when let_assert!( - Err(err) = validate(&*open_repository, &repo_details, &repo_config), + Err(err) = validate( + &*open_repository, + &repo_details, + &repo_config, + git::graph::Log::default() + ), "validate" ); @@ -394,7 +424,12 @@ mod positions { //when let_assert!( - Err(err) = validate(&*open_repository, &repo_details, &repo_config), + Err(err) = validate( + &*open_repository, + &repo_details, + &repo_config, + git::graph::Log::default() + ), "validate" ); @@ -481,7 +516,12 @@ mod positions { //when let_assert!( - Err(err) = validate(&*open_repository, &repo_details, &repo_config), + Err(err) = validate( + &*open_repository, + &repo_details, + &repo_config, + git::graph::Log::default() + ), "validate" ); @@ -567,7 +607,12 @@ mod positions { //when let_assert!( - Ok(positions) = validate(&*open_repository, &repo_details, &repo_config), + Ok(positions) = validate( + &*open_repository, + &repo_details, + &repo_config, + git::graph::Log::default() + ), "validate" ); @@ -626,7 +671,12 @@ mod positions { //when let_assert!( - Ok(positions) = validate(&*open_repository, &repo_details, &repo_config), + Ok(positions) = validate( + &*open_repository, + &repo_details, + &repo_config, + git::graph::Log::default() + ), "validate" );