From e7df741ae0ab46e7822c9ac37677c30ff3dacf58 Mon Sep 17 00:00:00 2001 From: Heliozoa Date: Tue, 9 Jan 2024 10:39:58 +0200 Subject: [PATCH] Reworked interface to accommodate for courses.mooc.fi --- .gitignore | 1 + Cargo.lock | 1315 ++++++++++++----------- Cargo.toml | 13 +- src/cli.rs | 51 +- src/client.rs | 43 +- src/commands.rs | 108 +- src/commands/courses.rs | 109 -- src/commands/download.rs | 50 +- src/commands/exercises.rs | 286 ----- src/commands/login.rs | 2 + src/commands/logout.rs | 2 + src/commands/mooc.rs | 42 +- src/commands/mooc/course_exercises.rs | 33 - src/commands/mooc/courses.rs | 28 - src/commands/mooc/download_exercises.rs | 50 +- src/commands/mooc/submit_exercise.rs | 23 +- src/commands/mooc/update_exercises.rs | 36 +- src/commands/organization.rs | 102 -- src/commands/paste.rs | 22 +- src/commands/submit.rs | 22 +- src/commands/test.rs | 2 +- src/commands/update.rs | 80 +- src/commands/util.rs | 93 +- src/commands/util/organization.rs | 47 + src/config.rs | 38 +- src/interactive.rs | 2 +- src/interactive/prompt.rs | 53 +- src/interactive/state.rs | 23 +- src/main.rs | 8 + 29 files changed, 1175 insertions(+), 1509 deletions(-) delete mode 100644 src/commands/courses.rs delete mode 100644 src/commands/exercises.rs delete mode 100644 src/commands/mooc/course_exercises.rs delete mode 100644 src/commands/mooc/courses.rs delete mode 100644 src/commands/organization.rs create mode 100644 src/commands/util/organization.rs diff --git a/.gitignore b/.gitignore index ea8c4bf..1a4595e 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ /target +*.log diff --git a/Cargo.lock b/Cargo.lock index e2ae392..9064182 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,9 +4,9 @@ version = 3 [[package]] name = "addr2line" -version = "0.20.0" +version = "0.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4fa78e18c64fce05e902adecd7a5eed15a5e0a3439f7b0e169f0252214865e3" +checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" dependencies = [ "gimli", ] @@ -28,15 +28,33 @@ dependencies = [ "cpufeatures", ] +[[package]] +name = "ahash" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77c3a9648d43b9cd48db467b3f87fdd6e146bcc88ab0180006cef2179fe11d01" +dependencies = [ + "cfg-if", + "once_cell", + "version_check", + "zerocopy", +] + [[package]] name = "aho-corasick" -version = "1.0.2" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43f6cb1bf222025340178f382c426f13757b2960e89779dfcb319c32542a5a41" +checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0" dependencies = [ "memchr", ] +[[package]] +name = "allocator-api2" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0942ffc6dcaadf03badf6e6a2d0228460359d5e34b57ccdc720b7382dfbd5ec5" + [[package]] name = "android-tzdata" version = "0.1.1" @@ -54,58 +72,57 @@ dependencies = [ [[package]] name = "anstream" -version = "0.3.2" +version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ca84f3628370c59db74ee214b3263d58f9aadd9b4fe7e711fd87dc452b7f163" +checksum = "d664a92ecae85fd0a7392615844904654d1d5f5514837f471ddef4a057aba1b6" dependencies = [ "anstyle", "anstyle-parse", "anstyle-query", "anstyle-wincon", "colorchoice", - "is-terminal", "utf8parse", ] [[package]] name = "anstyle" -version = "1.0.1" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a30da5c5f2d5e72842e00bcb57657162cdabef0931f40e2deb9b4140440cecd" +checksum = "7079075b41f533b8c61d2a4d073c4676e1f8b249ff94a393b0595db304e0dd87" [[package]] name = "anstyle-parse" -version = "0.2.1" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "938874ff5980b03a87c5524b3ae5b59cf99b1d6bc836848df7bc5ada9643c333" +checksum = "c75ac65da39e5fe5ab759307499ddad880d724eed2f6ce5b5e8a26f4f387928c" dependencies = [ "utf8parse", ] [[package]] name = "anstyle-query" -version = "1.0.0" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b" +checksum = "e28923312444cdd728e4738b3f9c9cac739500909bb3d3c94b43551b16517648" dependencies = [ - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] name = "anstyle-wincon" -version = "1.0.1" +version = "3.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "180abfa45703aebe0093f79badacc01b8fd4ea2e35118747e5811127f926e188" +checksum = "1cd54b81ec8d6180e24654d0b371ad22fc3dd083b6ff8ba325b72e00c87660a7" dependencies = [ "anstyle", - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] name = "anyhow" -version = "1.0.71" +version = "1.0.79" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c7d0618f0e0b7e8ff11427422b64564d5fb0be1940354bfe2e0529b18a9d9b8" +checksum = "080e9890a082662b09c1ad45f567faeeb47f22b5fb23895fbe1e651e718e25ca" dependencies = [ "backtrace", ] @@ -134,9 +151,9 @@ dependencies = [ [[package]] name = "assert_cmd" -version = "2.0.11" +version = "2.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86d6b683edf8d1119fe420a94f8a7e389239666aa72e65495d91c00462510151" +checksum = "88903cb14723e4d4003335bb7f8a14f27691649105346a0f0957466c096adfe6" dependencies = [ "anstyle", "bstr", @@ -155,9 +172,9 @@ checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" [[package]] name = "backtrace" -version = "0.3.68" +version = "0.3.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4319208da049c43661739c5fade2ba182f09d1dc2299b32298d3a31692b17e12" +checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837" dependencies = [ "addr2line", "cc", @@ -176,9 +193,9 @@ checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" [[package]] name = "base64" -version = "0.21.2" +version = "0.21.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "604178f6c5c21f02dc555784810edfb88d34ac2c73b2eae109655649ee73ce3d" +checksum = "c79fed4cdb43e993fcdadc7e58a09fd0e3e649c4436fa11da71c9f1f3ee7feb9" [[package]] name = "base64ct" @@ -194,22 +211,21 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.3.3" +version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "630be753d4e58660abd17930c71b647fe46c27ea6b63cc59e1e3851406972e42" +checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07" [[package]] name = "blake3" -version = "1.4.1" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "199c42ab6972d92c9f8995f086273d25c42fc0f7b2a1fcefba465c1352d25ba5" +checksum = "0231f06152bf547e9c2b5194f247cd97aacf6dcd8b15d8e5ec0663f64580da87" dependencies = [ "arrayref", "arrayvec", "cc", "cfg-if", "constant_time_eq 0.3.0", - "digest", ] [[package]] @@ -223,9 +239,9 @@ dependencies = [ [[package]] name = "bstr" -version = "1.6.0" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6798148dccfbff0fae41c7574d2fa8f1ef3492fba0face179de5d8d447d67b05" +checksum = "c48f0051a4b4c5e0b6d365cd04af53aeaa209e3cc15ec2cdb69e73cc87fbd0dc" dependencies = [ "memchr", "regex-automata", @@ -234,21 +250,21 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.13.0" +version = "3.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3e2c3daef883ecc1b5d58c15adae93470a91d425f3532ba1695849656af3fc1" +checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec" [[package]] name = "byteorder" -version = "1.4.3" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" -version = "1.4.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" +checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" [[package]] name = "bzip2" @@ -279,11 +295,12 @@ checksum = "df8670b8c7b9dae1793364eafadf7239c40d669904660c5960d74cfd80b46a53" [[package]] name = "cc" -version = "1.0.79" +version = "1.0.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" +checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" dependencies = [ "jobserver", + "libc", ] [[package]] @@ -300,18 +317,17 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "chrono" -version = "0.4.26" +version = "0.4.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec837a71355b28f6556dbd569b37b3f363091c0bd4b2e735674521b4c5fd9bc5" +checksum = "7f2c685bad3eb3d45a01354cedb7d5faa66194d1d58ba6e267a8de788f79db38" dependencies = [ "android-tzdata", "iana-time-zone", "js-sys", "num-traits", "serde", - "time 0.1.45", "wasm-bindgen", - "winapi", + "windows-targets 0.48.5", ] [[package]] @@ -326,20 +342,19 @@ dependencies = [ [[package]] name = "clap" -version = "4.3.11" +version = "4.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1640e5cc7fb47dbb8338fd471b105e7ed6c3cb2aeb00c2e067127ffd3764a05d" +checksum = "33e92c5c1a78c62968ec57dbc2440366a2d6e5a23faf829970ff1585dc6b18e2" dependencies = [ "clap_builder", "clap_derive", - "once_cell", ] [[package]] name = "clap_builder" -version = "4.3.11" +version = "4.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "98c59138d527eeaf9b53f35a77fcc1fad9d883116070c63d5de1c7dc7b00c72b" +checksum = "f4323769dc8a61e2c39ad7dc26f6f2800524691a44d74fe3d1071a5c24db6370" dependencies = [ "anstream", "anstyle", @@ -349,30 +364,30 @@ dependencies = [ [[package]] name = "clap_complete" -version = "4.3.2" +version = "4.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fc443334c81a804575546c5a8a79b4913b50e28d69232903604cada1de817ce" +checksum = "97aeaa95557bd02f23fbb662f981670c3d20c5a26e69f7354b28f57092437fcd" dependencies = [ "clap", ] [[package]] name = "clap_derive" -version = "4.3.2" +version = "4.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8cd2b2a819ad6eec39e8f1d6b53001af1e5469f8c177579cdaeb313115b825f" +checksum = "cf9804afaaf59a91e75b022a30fb7229a7901f60c755489cc61c9b423b836442" dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.25", + "syn 2.0.48", ] [[package]] name = "clap_lex" -version = "0.5.0" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2da6da31387c7e4ef160ffab6d5e7f00c42626fe39aea70a7b0f1773f7dd6c1b" +checksum = "702fc72eb24e5a1e48ce58027a675bc24edd52096d5397d4aea7c6dd9eca0bd1" [[package]] name = "colorchoice" @@ -382,11 +397,10 @@ checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" [[package]] name = "colored" -version = "2.0.4" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2674ec482fbc38012cf31e6c42ba0177b431a0cb6f15fe40efa5aab1bda516f6" +checksum = "cbf2150cce219b664a8a70df7a1f933836724b503f8a413af9365b4dcc4d90b8" dependencies = [ - "is-terminal", "lazy_static", "windows-sys 0.48.0", ] @@ -423,7 +437,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e859cd57d0710d9e06c381b550c06e76992472a8c6d527aecd2fc673dcc231fb" dependencies = [ "percent-encoding", - "time 0.3.23", + "time", "version_check", ] @@ -440,21 +454,31 @@ dependencies = [ "serde", "serde_derive", "serde_json", - "time 0.3.23", + "time", "url", ] +[[package]] +name = "core-foundation" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "core-foundation-sys" -version = "0.8.4" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa" +checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" [[package]] name = "cpufeatures" -version = "0.2.9" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a17b76ff3a4162b0b27f354a0c87015ddad39d35f9c0c36607a3bdd175dde1f1" +checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504" dependencies = [ "libc", ] @@ -470,36 +494,17 @@ dependencies = [ [[package]] name = "crossbeam-utils" -version = "0.8.16" +version = "0.8.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a22b2d63d4d1dc0b7f1b6b2747dd0088008a9be28b6ddf0b1e7d335e3037294" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "crossterm" -version = "0.25.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e64e6c0fbe2c17357405f7c758c1ef960fce08bdfb2c03d88d2a18d7e09c4b67" -dependencies = [ - "bitflags 1.3.2", - "crossterm_winapi", - "libc", - "mio", - "parking_lot", - "signal-hook", - "signal-hook-mio", - "winapi", -] +checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345" [[package]] name = "crossterm" -version = "0.26.1" +version = "0.27.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a84cda67535339806297f1b331d6dd6320470d2a0fe65381e79ee9e156dd3d13" +checksum = "f476fe445d41c9e991fd07515a6f463074b782242ccf4a5b7b1d1012e70824df" dependencies = [ - "bitflags 1.3.2", + "bitflags 2.4.1", "crossterm_winapi", "libc", "mio", @@ -528,6 +533,25 @@ dependencies = [ "typenum", ] +[[package]] +name = "ctrlc" +version = "3.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b467862cc8610ca6fc9a1532d7777cee0804e678ab45410897b9396495994a0b" +dependencies = [ + "nix", + "windows-sys 0.52.0", +] + +[[package]] +name = "deranged" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" +dependencies = [ + "powerfmt", +] + [[package]] name = "difflib" version = "0.4.0" @@ -580,15 +604,15 @@ checksum = "56ce8c6da7551ec6c462cbaf3bfbc75131ebbfa1c944aeaa9dab51ca1c5f0c3b" [[package]] name = "dyn-clone" -version = "1.0.11" +version = "1.0.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68b0cf012f1230e43cd00ebb729c6bb58707ecfa8ad08b52ef3a4ccd2697fc30" +checksum = "545b22097d44f8a9581187cdf93de7a71e4722bf51200cfaba810865b49a495d" [[package]] name = "either" -version = "1.8.1" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91" +checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" [[package]] name = "encode_unicode" @@ -598,9 +622,9 @@ checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f" [[package]] name = "encoding_rs" -version = "0.8.32" +version = "0.8.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "071a31f4ee85403370b58aca746f01041ede6f0da2730960ad001edc2b71b394" +checksum = "7268b386296a025e474d5140678f75d6de9493ae55a5d709eeb9dd08149945e1" dependencies = [ "cfg-if", ] @@ -613,62 +637,48 @@ checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" [[package]] name = "errno" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4bcfec3a70f97c962c307b2d2c56e358cf1d00b558d74262b5f929ee8cc7e73a" -dependencies = [ - "errno-dragonfly", - "libc", - "windows-sys 0.48.0", -] - -[[package]] -name = "errno-dragonfly" -version = "0.1.2" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" +checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245" dependencies = [ - "cc", "libc", + "windows-sys 0.52.0", ] [[package]] name = "fastrand" -version = "1.9.0" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be" -dependencies = [ - "instant", -] +checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5" [[package]] name = "fd-lock" -version = "4.0.0" +version = "4.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b0377f1edc77dbd1118507bc7a66e4ab64d2b90c66f90726dc801e73a8c68f9" +checksum = "7e5768da2206272c81ef0b5e951a41862938a6070da63bcea197899942d3b947" dependencies = [ "cfg-if", - "rustix 0.38.3", - "windows-sys 0.48.0", + "rustix", + "windows-sys 0.52.0", ] [[package]] name = "filetime" -version = "0.2.21" +version = "0.2.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5cbc844cecaee9d4443931972e1289c8ff485cb4cc2767cb03ca139ed6885153" +checksum = "1ee447700ac8aa0b2f2bd7bc4462ad686ba06baa6727ac149a2d6277f0d240fd" dependencies = [ "cfg-if", "libc", - "redox_syscall 0.2.16", - "windows-sys 0.48.0", + "redox_syscall", + "windows-sys 0.52.0", ] [[package]] name = "flate2" -version = "1.0.26" +version = "1.0.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b9429470923de8e8cbd4d2dc513535400b4b3fef0319fb5c4e1f520a7bef743" +checksum = "46303f565772937ffe1d394a4fac6f411c6013172fadde9dcdb1e147a086940e" dependencies = [ "crc32fast", "miniz_oxide", @@ -676,9 +686,9 @@ dependencies = [ [[package]] name = "flexi_logger" -version = "0.25.5" +version = "0.27.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37e7b68b1f7ce9c62856598e99cd6742b9cedb6186b47aa989a82640f20bfa9b" +checksum = "2ac35b454b60e1836602173e2eb7ef531173388c0212e02ec7f9fac086159ee5" dependencies = [ "chrono", "glob", @@ -707,9 +717,9 @@ checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" [[package]] name = "form_urlencoded" -version = "1.2.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a62bc1cf6f830c2ec14a513a9fb124d0a213a629668a4186f329db21fe045652" +checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" dependencies = [ "percent-encoding", ] @@ -722,9 +732,9 @@ checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c" [[package]] name = "futures" -version = "0.3.28" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23342abe12aba583913b2e62f22225ff9c950774065e4bfb61a19cd9770fec40" +checksum = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0" dependencies = [ "futures-channel", "futures-core", @@ -737,9 +747,9 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.28" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "955518d47e09b25bbebc7a18df10b81f0c766eaf4c4f1cccef2fca5f2a4fb5f2" +checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" dependencies = [ "futures-core", "futures-sink", @@ -747,15 +757,15 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.28" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4bca583b7e26f571124fe5b7561d49cb2868d79116cfa0eefce955557c6fee8c" +checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" [[package]] name = "futures-executor" -version = "0.3.28" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ccecee823288125bd88b4d7f565c9e58e41858e47ab72e8ea2d64e93624386e0" +checksum = "a576fc72ae164fca6b9db127eaa9a9dda0d61316034f33a0a0d4eda41f02b01d" dependencies = [ "futures-core", "futures-task", @@ -764,38 +774,38 @@ dependencies = [ [[package]] name = "futures-io" -version = "0.3.28" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fff74096e71ed47f8e023204cfd0aa1289cd54ae5430a9523be060cdb849964" +checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" [[package]] name = "futures-macro" -version = "0.3.28" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72" +checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ "proc-macro2", "quote", - "syn 2.0.25", + "syn 2.0.48", ] [[package]] name = "futures-sink" -version = "0.3.28" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f43be4fe21a13b9781a69afa4985b0f6ee0e1afab2c6f454a8cf30e2b2237b6e" +checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5" [[package]] name = "futures-task" -version = "0.3.28" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76d3d132be6c0e6aa1534069c705a74a5997a356c0dc2f86a47765e5617c5b65" +checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" [[package]] name = "futures-util" -version = "0.3.28" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26b01e40b772d54cf6c6d721c1d1abd0647a0106a12ecaa1c186273392a69533" +checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" dependencies = [ "futures-channel", "futures-core", @@ -821,22 +831,22 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.10" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427" +checksum = "190092ea657667030ac6a35e305e62fc4dd69fd98ac98631e5d3a2b1575a12b5" dependencies = [ "cfg-if", "js-sys", "libc", - "wasi 0.11.0+wasi-snapshot-preview1", + "wasi", "wasm-bindgen", ] [[package]] name = "gimli" -version = "0.27.3" +version = "0.28.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6c80984affa11d98d1b88b66ac8853f143217b399d3c74116778ff8fdb4ed2e" +checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" [[package]] name = "glob" @@ -846,9 +856,9 @@ checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" [[package]] name = "h2" -version = "0.3.20" +version = "0.3.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97ec8491ebaf99c8eaa73058b045fe58073cd6be7f596ac993ced0b0a0c01049" +checksum = "4d6250322ef6e60f93f9a2162799302cd6f68f79f6e5d85c8c16f14d1d958178" dependencies = [ "bytes", "fnv", @@ -856,7 +866,7 @@ dependencies = [ "futures-sink", "futures-util", "http", - "indexmap 1.9.3", + "indexmap", "slab", "tokio", "tokio-util", @@ -865,15 +875,13 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.12.3" +version = "0.14.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" - -[[package]] -name = "hashbrown" -version = "0.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c6201b9ff9fd90a5a3bac2e56a830d0caa509576f0e503818ee82c181b3437a" +checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" +dependencies = [ + "ahash", + "allocator-api2", +] [[package]] name = "heck" @@ -883,9 +891,9 @@ checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" [[package]] name = "hermit-abi" -version = "0.3.2" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "443144c8cdadd93ebf52ddb4056d257f5b52c04d3c804e657d19eb73fc33668b" +checksum = "d77f7ec81a6d05a3abb01ab6eb7590f6083d08449fe5a1c8b1e620283546ccb7" [[package]] name = "hex" @@ -904,9 +912,9 @@ dependencies = [ [[package]] name = "http" -version = "0.2.9" +version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd6effc99afb63425aff9b05836f029929e345a6148a14b7ecd5ab67af944482" +checksum = "8947b1a6fad4393052c7ba1f4cd97bed3e953a95c79c92ad9b051a04611d9fbb" dependencies = [ "bytes", "fnv", @@ -915,9 +923,9 @@ dependencies = [ [[package]] name = "http-body" -version = "0.4.5" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5f38f16d184e36f2408a55281cd658ecbd3ca05cce6d6510a176eca393e26d1" +checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2" dependencies = [ "bytes", "http", @@ -932,15 +940,15 @@ checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" [[package]] name = "httpdate" -version = "1.0.2" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" +checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" [[package]] name = "hyper" -version = "0.14.27" +version = "0.14.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffb1cfd654a8219eaef89881fdb3bb3b1cdc5fa75ded05d6933b2b382e395468" +checksum = "bf96e135eb83a2a8ddf766e426a841d8ddd7449d5f00d34ea02b41d2f19eef80" dependencies = [ "bytes", "futures-channel", @@ -962,9 +970,9 @@ dependencies = [ [[package]] name = "hyper-rustls" -version = "0.24.1" +version = "0.24.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d78e1e73ec14cf7375674f74d7dde185c8206fd9dea6fb6295e8a98098aaa97" +checksum = "ec3efd23720e2049821a693cbc7e65ea87c72f1c58ff2f9522ff332b1491e590" dependencies = [ "futures-util", "http", @@ -976,16 +984,16 @@ dependencies = [ [[package]] name = "iana-time-zone" -version = "0.1.57" +version = "0.1.59" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fad5b825842d2b38bd206f3e81d6957625fd7f0a361e345c30e01a0ae2dd613" +checksum = "b6a67363e2aa4443928ce15e57ebae94fd8949958fd1223c4cfc0cd473ad7539" dependencies = [ "android_system_properties", "core-foundation-sys", "iana-time-zone-haiku", "js-sys", "wasm-bindgen", - "windows", + "windows-core", ] [[package]] @@ -1020,9 +1028,9 @@ dependencies = [ [[package]] name = "idna" -version = "0.4.0" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d20d6b07bfbc108882d88ed8e37d39636dcc260e15e30c45e6ba089610b917c" +checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" dependencies = [ "unicode-bidi", "unicode-normalization", @@ -1030,41 +1038,31 @@ dependencies = [ [[package]] name = "impl-enum" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab90bad167f9ec05c07777e07aaa49e01d0c525b2ef1a00d96a972db8bb8b561" +checksum = "0fd48df611bdfc457ea8c24c8c26e5c9d64857454fa8221da7db463d134371f3" dependencies = [ "heck", "proc-macro2", "quote", - "syn 1.0.109", -] - -[[package]] -name = "indexmap" -version = "1.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" -dependencies = [ - "autocfg", - "hashbrown 0.12.3", + "syn 2.0.48", ] [[package]] name = "indexmap" -version = "2.0.0" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5477fe2230a79769d8dc68e0eabf5437907c0457a5614a9e8dddb67f65eb65d" +checksum = "d530e1a18b1cb4c484e6e34556a0d948706958449fca0cab753d649f2bce3d1f" dependencies = [ "equivalent", - "hashbrown 0.14.0", + "hashbrown", ] [[package]] name = "indicatif" -version = "0.17.5" +version = "0.17.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ff8cc23a7393a397ed1d7f56e6365cba772aba9f9912ab968b03043c395d057" +checksum = "fb28741c9db9a713d93deb3bb9515c20788cef5815265bee4980e87bde7e0f25" dependencies = [ "console", "instant", @@ -1073,6 +1071,12 @@ dependencies = [ "unicode-width", ] +[[package]] +name = "indoc" +version = "2.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e186cfbae8084e513daff4240b4797e342f988cecda4fb6c939150f96315fd8" + [[package]] name = "inout" version = "0.1.3" @@ -1091,63 +1095,61 @@ dependencies = [ "cfg-if", ] -[[package]] -name = "io-lifetimes" -version = "1.0.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2" -dependencies = [ - "hermit-abi", - "libc", - "windows-sys 0.48.0", -] - [[package]] name = "ipnet" -version = "2.8.0" +version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28b29a3cd74f0f4598934efe3aeba42bae0eb4680554128851ebbecb02af14e6" +checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3" [[package]] name = "is-terminal" -version = "0.4.9" +version = "0.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb0889898416213fab133e1d33a0e5858a48177452750691bde3666d0fdbaf8b" +checksum = "0bad00257d07be169d870ab665980b06cdb366d792ad690bf2e76876dc503455" dependencies = [ "hermit-abi", - "rustix 0.38.3", - "windows-sys 0.48.0", + "rustix", + "windows-sys 0.52.0", ] [[package]] name = "isolang" -version = "2.3.0" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f80f221db1bc708b71128757b9396727c04de86968081e18e89b0575e03be071" +checksum = "fe50d48c77760c55188549098b9a7f6e37ae980c586a24693d6b01c3b2010c3c" dependencies = [ "phf", ] [[package]] name = "itertools" -version = "0.10.5" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1c173a5686ce8bfa551b3563d0c2170bf24ca44da99c7ca4bfdab5418c3fe57" +dependencies = [ + "either", +] + +[[package]] +name = "itertools" +version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +checksum = "25db6b064527c5d482d0423354fcd07a89a2dfe07b67892e62411946db7f07b0" dependencies = [ "either", ] [[package]] name = "itoa" -version = "1.0.8" +version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62b02a5381cc465bd3041d84623d0fa3b66738b52b8e2fc3bab8ad63ab032f4a" +checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c" [[package]] name = "j4rs" -version = "0.16.0" +version = "0.17.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fbb7f40761e966881d96153dba31b83f012de43d12f8ab262551a99bc9651ca1" +checksum = "b1203f16911f3650740c8f6eb11be7c0914bd40b03007b6aed5363c764f4046b" dependencies = [ "cesu8", "dirs", @@ -1184,18 +1186,18 @@ checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" [[package]] name = "jobserver" -version = "0.1.26" +version = "0.1.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "936cfd212a0155903bcbc060e316fb6cc7cbf2e1907329391ebadc1fe0ce77c2" +checksum = "8c37f63953c4c63420ed5fd3d6d398c719489b9f872b9fa683262f8edd363c7d" dependencies = [ "libc", ] [[package]] name = "js-sys" -version = "0.3.64" +version = "0.3.66" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5f195fe497f702db0f318b07fdd68edb16955aed830df8363d837542f8f935a" +checksum = "cee9c64da59eae3b50095c18d3e74f8b73c0b86d2792824ff01bbce68ba229ca" dependencies = [ "wasm-bindgen", ] @@ -1223,37 +1225,42 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.147" +version = "0.2.152" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" +checksum = "13e3bf6590cbc649f4d1a3eefc9d5d6eb746f5200ffb04e5e142700b8faa56e7" [[package]] name = "libloading" -version = "0.8.0" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d580318f95776505201b28cf98eb1fa5e4be3b689633ba6a3e6cd880ff22d8cb" +checksum = "c571b676ddfc9a8c12f1f3d3085a7b163966a8fd8098a90640953ce5f6170161" dependencies = [ "cfg-if", "windows-sys 0.48.0", ] [[package]] -name = "linux-raw-sys" -version = "0.3.8" +name = "libredox" +version = "0.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519" +checksum = "85c833ca1e66078851dba29046874e38f08b2c883700aa29a03ddd3b23814ee8" +dependencies = [ + "bitflags 2.4.1", + "libc", + "redox_syscall", +] [[package]] name = "linux-raw-sys" -version = "0.4.3" +version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09fc20d2ca12cb9f044c93e3bd6d32d523e6e2ec3db4f7b2939cd99026ecd3f0" +checksum = "c4cd1a83af159aa67994778be9070f0ae1bd732942279cabb14f86f986a21456" [[package]] name = "lock_api" -version = "0.4.10" +version = "0.4.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1cc9717a20b1bb222f333e6a92fd32f7d8a18ddc5a3191a11af45dcbf4dcd16" +checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45" dependencies = [ "autocfg", "scopeguard", @@ -1261,9 +1268,18 @@ dependencies = [ [[package]] name = "log" -version = "0.4.19" +version = "0.4.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b06a4cde4c0f271a446782e3eff8de789548ce57dbc8eca9292c27f4a42004b4" +checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" + +[[package]] +name = "lru" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2994eeba8ed550fd9b47a0b38f0242bc3344e496483c6180b69139cc2fa5d1d7" +dependencies = [ + "hashbrown", +] [[package]] name = "matches" @@ -1279,18 +1295,9 @@ checksum = "490cc448043f947bae3cbee9c203358d62dbee0db12107a74be5c30ccfd09771" [[package]] name = "memchr" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" - -[[package]] -name = "memoffset" -version = "0.7.1" +version = "2.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5de893c32cde5f383baa4c04c5d6dbdd735cfd4a794b0debdb2bb1b421da5ff4" -dependencies = [ - "autocfg", -] +checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149" [[package]] name = "mime" @@ -1325,27 +1332,26 @@ dependencies = [ [[package]] name = "mio" -version = "0.8.8" +version = "0.8.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "927a765cd3fc26206e66b296465fa9d3e5ab003e651c1b3c060e7956d96b19d2" +checksum = "8f3d0b296e374a4e6f3c7b0a1f5a51d748a0d34c85e7dc48fc3fa9a87657fe09" dependencies = [ "libc", "log", - "wasi 0.11.0+wasi-snapshot-preview1", + "wasi", "windows-sys 0.48.0", ] [[package]] name = "mockito" -version = "1.1.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09c762b6267c4593555bb38f1df19e9318985bc4de60b5e8462890856a9a5b4c" +checksum = "f8d3038e23466858569c2d30a537f691fa0d53b51626630ae08262943e3bbb8b" dependencies = [ "assert-json-diff", "colored", "futures", "hyper", - "lazy_static", "log", "rand", "regex", @@ -1358,7 +1364,7 @@ dependencies = [ [[package]] name = "mooc-langs-api" version = "0.1.0" -source = "git+https://github.com/rage/secret-project-331.git?rev=df8bd2b0c12ba8652e0e46b7ccba25dea1547dc6#df8bd2b0c12ba8652e0e46b7ccba25dea1547dc6" +source = "git+https://github.com/rage/secret-project-331.git?rev=778a5820a8b7422cbf9f1c786da437833ced5ae9#778a5820a8b7422cbf9f1c786da437833ced5ae9" dependencies = [ "chrono", "oauth2", @@ -1370,16 +1376,13 @@ dependencies = [ [[package]] name = "nix" -version = "0.26.2" +version = "0.27.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfdda3d196821d6af13126e40375cdf7da646a96114af134d5f417a9a1dc8e1a" +checksum = "2eb04e9c688eff1c89d72b407f168cf79bb9e867a9d3323ed6c01519eb9cc053" dependencies = [ - "bitflags 1.3.2", + "bitflags 2.4.1", "cfg-if", "libc", - "memoffset", - "pin-utils", - "static_assertions", ] [[package]] @@ -1400,18 +1403,18 @@ checksum = "61807f77802ff30975e01f4f071c8ba10c022052f98b3294119f3e615d13e5be" [[package]] name = "nu-ansi-term" -version = "0.47.0" +version = "0.49.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1df031e117bca634c262e9bd3173776844b6c17a90b3741c9163663b4385af76" +checksum = "c073d3c1930d0751774acf49e66653acecb416c3a54c6ec095a9b11caddb5a68" dependencies = [ - "windows-sys 0.45.0", + "windows-sys 0.48.0", ] [[package]] name = "num-traits" -version = "0.2.15" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" +checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c" dependencies = [ "autocfg", ] @@ -1434,9 +1437,9 @@ checksum = "830b246a0e5f20af87141b25c173cd1b609bd7779a4617d6ec582abaf90870f3" [[package]] name = "oauth2" -version = "4.4.1" +version = "4.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09a6e2a2b13a56ebeabba9142f911745be6456163fd6c3d361274ebcd891a80c" +checksum = "c38841cdd844847e3e7c8d29cef9dcfed8877f8f56f9071f77843ecf3baf937f" dependencies = [ "base64 0.13.1", "chrono", @@ -1454,18 +1457,18 @@ dependencies = [ [[package]] name = "object" -version = "0.31.1" +version = "0.32.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8bda667d9f2b5051b8833f59f3bf748b28ef54f850f4fcb389a252aa383866d1" +checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441" dependencies = [ "memchr", ] [[package]] name = "once_cell" -version = "1.18.0" +version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" [[package]] name = "option-ext" @@ -1485,15 +1488,15 @@ dependencies = [ [[package]] name = "parking_lot_core" -version = "0.9.8" +version = "0.9.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93f00c865fe7cabf650081affecd3871070f26767e7b2070a3ffae14c654b447" +checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e" dependencies = [ "cfg-if", "libc", - "redox_syscall 0.3.5", + "redox_syscall", "smallvec", - "windows-targets 0.48.1", + "windows-targets 0.48.5", ] [[package]] @@ -1507,6 +1510,12 @@ dependencies = [ "subtle", ] +[[package]] +name = "paste" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" + [[package]] name = "pbkdf2" version = "0.11.0" @@ -1521,9 +1530,9 @@ dependencies = [ [[package]] name = "percent-encoding" -version = "2.3.0" +version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94" +checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" [[package]] name = "phf" @@ -1545,9 +1554,9 @@ dependencies = [ [[package]] name = "pin-project-lite" -version = "0.2.10" +version = "0.2.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c40d25201921e5ff0c862a505c6557ea88568a4e3ace775ab55e93f2f4f9d57" +checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" [[package]] name = "pin-utils" @@ -1557,15 +1566,21 @@ checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" [[package]] name = "pkg-config" -version = "0.3.27" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964" +checksum = "69d3587f8a9e599cc7ec2c00e331f71c4e69a5f9a4b8a6efd5b07466b9736f9a" [[package]] name = "portable-atomic" -version = "1.3.3" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "767eb9f07d4a5ebcb39bbf2d452058a93c011373abf6832e24194a1c3f004794" +checksum = "7170ef9988bc169ba16dd36a7fa041e5c4cbeb6a35b76d4c03daded371eae7c0" + +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" [[package]] name = "ppv-lite86" @@ -1575,14 +1590,14 @@ checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" [[package]] name = "predicates" -version = "3.0.3" +version = "3.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09963355b9f467184c04017ced4a2ba2d75cbcb4e7462690d388233253d4b1a9" +checksum = "6dfc28575c2e3f19cb3c73b93af36460ae898d426eba6fc15b9bd2a5220758a0" dependencies = [ "anstyle", "difflib", "float-cmp", - "itertools", + "itertools 0.11.0", "normalize-line-endings", "predicates-core", "regex", @@ -1606,9 +1621,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.64" +version = "1.0.76" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78803b62cbf1f46fde80d7c0e803111524b9877184cfe7c3033659490ac7a7da" +checksum = "95fc56cda0b5c3325f5fbbd7ff9fda9e02bb00bb3dac51252d2f1bfa1cb8cc8c" dependencies = [ "unicode-ident", ] @@ -1631,9 +1646,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.29" +version = "1.0.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "573015e8ab27661678357f27dc26460738fd2b6c86e46f386fde94cb5d913105" +checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" dependencies = [ "proc-macro2", ] @@ -1669,39 +1684,49 @@ dependencies = [ ] [[package]] -name = "redox_syscall" -version = "0.2.16" +name = "ratatui" +version = "0.25.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" +checksum = "a5659e52e4ba6e07b2dad9f1158f578ef84a73762625ddb51536019f34d180eb" dependencies = [ - "bitflags 1.3.2", + "bitflags 2.4.1", + "cassowary", + "crossterm", + "indoc", + "itertools 0.12.0", + "lru", + "paste", + "stability", + "strum", + "unicode-segmentation", + "unicode-width", ] [[package]] name = "redox_syscall" -version = "0.3.5" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" +checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" dependencies = [ "bitflags 1.3.2", ] [[package]] name = "redox_users" -version = "0.4.3" +version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b" +checksum = "a18479200779601e498ada4e8c1e1f50e3ee19deb0259c25825a98b5603b2cb4" dependencies = [ "getrandom", - "redox_syscall 0.2.16", + "libredox", "thiserror", ] [[package]] name = "regex" -version = "1.9.1" +version = "1.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2eae68fc220f7cf2532e4494aded17545fce192d59cd996e0fe7887f4ceb575" +checksum = "380b951a9c5e80ddfd6136919eef32310721aa4aacd4889a8d39124b026ab343" dependencies = [ "aho-corasick", "memchr", @@ -1711,9 +1736,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.3.2" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83d3daa6976cffb758ec878f108ba0e062a45b2d6ca3a2cca965338855476caf" +checksum = "5f804c7828047e88b2d32e2d7fe5a105da8ee3264f01902f796c8e067dc2483f" dependencies = [ "aho-corasick", "memchr", @@ -1722,17 +1747,17 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.7.4" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5ea92a5b6195c6ef2a0295ea818b312502c6fc94dde986c5553242e18fd4ce2" +checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" [[package]] name = "reqwest" -version = "0.11.18" +version = "0.11.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cde824a14b7c14f85caff81225f411faacc04a2013f41670f41443742b1c1c55" +checksum = "37b1ae8d9ac08420c66222fb9096fc5de435c3c48542bc5336c51892cffafb41" dependencies = [ - "base64 0.21.2", + "base64 0.21.6", "bytes", "cookie", "cookie_store", @@ -1757,6 +1782,7 @@ dependencies = [ "serde", "serde_json", "serde_urlencoded", + "system-configuration", "tokio", "tokio-rustls", "tower-service", @@ -1770,38 +1796,37 @@ dependencies = [ [[package]] name = "ring" -version = "0.16.20" +version = "0.17.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc" +checksum = "688c63d65483050968b2a8937f7995f443e27041a0f7700aa59b0822aedebb74" dependencies = [ "cc", + "getrandom", "libc", - "once_cell", "spin", "untrusted", - "web-sys", - "winapi", + "windows-sys 0.48.0", ] [[package]] name = "rpassword" -version = "7.2.0" +version = "7.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6678cf63ab3491898c0d021b493c94c9b221d91295294a2a5746eacbe5928322" +checksum = "80472be3c897911d0137b2d2b9055faf6eeac5b14e324073d83bc17b191d7e3f" dependencies = [ "libc", "rtoolbox", - "winapi", + "windows-sys 0.48.0", ] [[package]] name = "rtoolbox" -version = "0.0.1" +version = "0.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "034e22c514f5c0cb8a10ff341b9b048b5ceb21591f31c8f44c43b960f9b3524a" +checksum = "c247d24e63230cdb56463ae328478bd5eac8b8faa8c69461a77e8e323afac90e" dependencies = [ "libc", - "winapi", + "windows-sys 0.48.0", ] [[package]] @@ -1818,36 +1843,22 @@ checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" [[package]] name = "rustix" -version = "0.37.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d69718bf81c6127a49dc64e44a742e8bb9213c0ff8869a22c308f84c1d4ab06" -dependencies = [ - "bitflags 1.3.2", - "errno", - "io-lifetimes", - "libc", - "linux-raw-sys 0.3.8", - "windows-sys 0.48.0", -] - -[[package]] -name = "rustix" -version = "0.38.3" +version = "0.38.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac5ffa1efe7548069688cd7028f32591853cd7b5b756d41bcffd2353e4fc75b4" +checksum = "72e572a5e8ca657d7366229cdde4bd14c4eb5499a9573d4d366fe1b599daa316" dependencies = [ - "bitflags 2.3.3", + "bitflags 2.4.1", "errno", "libc", - "linux-raw-sys 0.4.3", - "windows-sys 0.48.0", + "linux-raw-sys", + "windows-sys 0.52.0", ] [[package]] name = "rustls" -version = "0.21.5" +version = "0.21.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79ea77c539259495ce8ca47f53e66ae0330a8819f67e23ac96ca02f50e7b7d36" +checksum = "f9d5a6813c0759e4609cd494e8e725babae6a2ca7b62a5536a13daaec6fcb7ba" dependencies = [ "log", "ring", @@ -1857,28 +1868,34 @@ dependencies = [ [[package]] name = "rustls-pemfile" -version = "1.0.3" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d3987094b1d07b653b7dfdc3f70ce9a1da9c51ac18c1b06b662e4f9a0e9f4b2" +checksum = "1c74cae0a4cf6ccbbf5f359f08efdf8ee7e1dc532573bf0db71968cb56b1448c" dependencies = [ - "base64 0.21.2", + "base64 0.21.6", ] [[package]] name = "rustls-webpki" -version = "0.101.1" +version = "0.101.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15f36a6828982f422756984e47912a7a51dcbc2a197aa791158f8ca61cd8204e" +checksum = "8b6275d1ee7a1cd780b64aca7726599a1dbc893b1e64144529e55c3c2f745765" dependencies = [ "ring", "untrusted", ] [[package]] -name = "ryu" +name = "rustversion" version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe232bdf6be8c8de797b22184ee71118d63780ea42ac85b61d1baa6d3b782ae9" +checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4" + +[[package]] +name = "ryu" +version = "1.0.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f98d2aa92eebf49b69786be48e4477826b256916e84a57ff2a4f21923b48eb4c" [[package]] name = "same-file" @@ -1891,9 +1908,9 @@ dependencies = [ [[package]] name = "schemars" -version = "0.8.12" +version = "0.8.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02c613288622e5f0c3fdc5dbd4db1c5fbe752746b1d1a56a0630b78fd00de44f" +checksum = "45a28f4c49489add4ce10783f7911893516f15afe45d015608d41faca6bc4d29" dependencies = [ "chrono", "dyn-clone", @@ -1904,9 +1921,9 @@ dependencies = [ [[package]] name = "schemars_derive" -version = "0.8.12" +version = "0.8.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "109da1e6b197438deb6db99952990c7f959572794b80ff93707d55a232545e7c" +checksum = "c767fd6fa65d9ccf9cf026122c1b555f2ef9a4f0cea69da4d7dbc3e258d30967" dependencies = [ "proc-macro2", "quote", @@ -1916,15 +1933,15 @@ dependencies = [ [[package]] name = "scopeguard" -version = "1.1.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "sct" -version = "0.7.0" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d53dcdb7c9f8158937a7981b48accfd39a43af418591a5d008c7b22b5e1b7ca4" +checksum = "da046153aa2352493d6cb7da4b6e5c0c057d8a1d0a9aa8560baffdd945acd414" dependencies = [ "ring", "untrusted", @@ -1932,9 +1949,9 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.171" +version = "1.0.195" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30e27d1e4fd7659406c492fd6cfaf2066ba8773de45ca75e855590f856dc34a9" +checksum = "63261df402c67811e9ac6def069e4786148c4563f4b50fd4bf30aa370d626b02" dependencies = [ "serde_derive", ] @@ -1953,13 +1970,13 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.171" +version = "1.0.195" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "389894603bd18c46fa56231694f8d827779c0951a667087194cf9de94ed24682" +checksum = "46fe8f8603d81ba86327b23a2e9cdf49e1255fb94a4c5f297f6ee0547178ea2c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.25", + "syn 2.0.48", ] [[package]] @@ -1975,9 +1992,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.100" +version = "1.0.111" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f1e14e89be7aa4c4b78bdbdc9eb5bf8517829a600ae8eaa39a6e1d960b5185c" +checksum = "176e46fa42316f18edd598015a5166857fc835ec732f5215eac6b7bdbf0a84f4" dependencies = [ "itoa", "ryu", @@ -1986,9 +2003,9 @@ dependencies = [ [[package]] name = "serde_path_to_error" -version = "0.1.13" +version = "0.1.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8acc4422959dd87a76cb117c191dcbffc20467f06c9100b76721dab370f24d3a" +checksum = "ebd154a240de39fdebcf5775d2675c204d7c13cf39a4c697be6493c8e734337c" dependencies = [ "itoa", "serde", @@ -1996,9 +2013,9 @@ dependencies = [ [[package]] name = "serde_spanned" -version = "0.6.3" +version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96426c9936fd7a0124915f9185ea1d20aa9445cc9821142f0a73bc9207a2e186" +checksum = "eb3622f419d1296904700073ea6cc23ad690adbd66f13ea683df73298736f0c1" dependencies = [ "serde", ] @@ -2017,11 +2034,11 @@ dependencies = [ [[package]] name = "serde_yaml" -version = "0.9.22" +version = "0.9.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "452e67b9c20c37fa79df53201dc03839651086ed9bbe92b3ca585ca9fdaa7d85" +checksum = "b1bf28c79a99f70ee1f1d83d10c875d2e70618417fda01ad1785e027579d9d38" dependencies = [ - "indexmap 2.0.0", + "indexmap", "itoa", "ryu", "serde", @@ -2030,9 +2047,9 @@ dependencies = [ [[package]] name = "sha1" -version = "0.10.5" +version = "0.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f04293dc80c3993519f2d7f6f511707ee7094fe0c6d3406feb330cdb3540eba3" +checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" dependencies = [ "cfg-if", "cpufeatures", @@ -2041,9 +2058,9 @@ dependencies = [ [[package]] name = "sha2" -version = "0.10.7" +version = "0.10.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "479fb9d862239e610720565ca91403019f2f00410f1864c5aa7479b950a76ed8" +checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" dependencies = [ "cfg-if", "cpufeatures", @@ -2062,9 +2079,9 @@ dependencies = [ [[package]] name = "signal-hook" -version = "0.3.15" +version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "732768f1176d21d09e076c23a93123d40bba92d50c4058da34d45c8de8e682b9" +checksum = "8621587d4798caf8eb44879d42e56b9a93ea5dcd315a6487c357130095b62801" dependencies = [ "libc", "signal-hook-registry", @@ -2092,52 +2109,56 @@ dependencies = [ [[package]] name = "similar" -version = "2.2.1" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "420acb44afdae038210c99e69aae24109f32f15500aa708e81d46c9f29d55fcf" +checksum = "32fea41aca09ee824cc9724996433064c89f7777e60762749a4170a14abbfa21" [[package]] name = "siphasher" -version = "0.3.10" +version = "0.3.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7bd3e3206899af3f8b12af284fafc038cc1dc2b41d1b89dd17297221c5d225de" +checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d" [[package]] name = "slab" -version = "0.4.8" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6528351c9bc8ab22353f9d776db39a20288e8d6c37ef8cfe3317cf875eecfc2d" +checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" dependencies = [ "autocfg", ] [[package]] name = "smallvec" -version = "1.11.0" +version = "1.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62bb4feee49fdd9f707ef802e22365a35de4b7b299de4763d44bfea899442ff9" +checksum = "4dccd0940a2dcdf68d092b8cbab7dc0ad8fa938bf95787e1b916b0e3d0e8e970" [[package]] name = "socket2" -version = "0.4.9" +version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64a4a911eed85daf18834cfaa86a79b7d266ff93ff5ba14005426219480ed662" +checksum = "7b5fac59a5cb5dd637972e5fca70daf0523c9067fcdc4842f053dae04a18f8e9" dependencies = [ "libc", - "winapi", + "windows-sys 0.48.0", ] [[package]] name = "spin" -version = "0.5.2" +version = "0.9.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" [[package]] -name = "static_assertions" -version = "1.1.0" +name = "stability" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" +checksum = "ebd1b177894da2a2d9120208c3386066af06a488255caabc5de8ddca22dbc3ce" +dependencies = [ + "quote", + "syn 1.0.109", +] [[package]] name = "strsim" @@ -2145,6 +2166,28 @@ version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" +[[package]] +name = "strum" +version = "0.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "290d54ea6f91c969195bdbcd7442c8c2a2ba87da8bf60a7ee86a235d4bc1e125" +dependencies = [ + "strum_macros", +] + +[[package]] +name = "strum_macros" +version = "0.25.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23dc1fa9ac9c169a78ba62f0b841814b7abae11bdd047b9c58f893439e309ea0" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "rustversion", + "syn 2.0.48", +] + [[package]] name = "subprocess" version = "0.2.9" @@ -2174,20 +2217,41 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.25" +version = "2.0.48" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15e3fc8c0c74267e2df136e5e5fb656a464158aa57624053375eb9c8c6e25ae2" +checksum = "0f3531638e407dfc0814761abb7c00a5b54992b849452a0646b7f65c9f770f3f" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] +[[package]] +name = "system-configuration" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7" +dependencies = [ + "bitflags 1.3.2", + "core-foundation", + "system-configuration-sys", +] + +[[package]] +name = "system-configuration-sys" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "tar" -version = "0.4.38" +version = "0.4.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b55807c0344e1e6c04d7c965f5289c39a8d94ae23ed5c0b57aabac549f871c6" +checksum = "b16afcea1f22891c49a00c751c7b63b2233284064f11a200fc624137c51e2ddb" dependencies = [ "filetime", "libc", @@ -2196,34 +2260,33 @@ dependencies = [ [[package]] name = "tempfile" -version = "3.6.0" +version = "3.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31c0432476357e58790aaa47a8efb0c5138f137343f3b5f23bd36a27e3b0a6d6" +checksum = "01ce4141aa927a6d1bd34a041795abd0db1cccba5d5f24b009f694bdf3a1f3fa" dependencies = [ - "autocfg", "cfg-if", "fastrand", - "redox_syscall 0.3.5", - "rustix 0.37.23", - "windows-sys 0.48.0", + "redox_syscall", + "rustix", + "windows-sys 0.52.0", ] [[package]] name = "termcolor" -version = "1.2.0" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be55cf8942feac5c765c2c993422806843c9a9a45d4d5c407ad6dd2ea95eb9b6" +checksum = "ff1bc3d3f05aff0403e8ac0d92ced918ec05b666a43f83297ccef5bea8a3d449" dependencies = [ "winapi-util", ] [[package]] name = "terminal_size" -version = "0.2.6" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e6bf6f19e9f8ed8d4048dc22981458ebcf406d67e94cd422e5ecd73d63b3237" +checksum = "21bebf2b7c9e0a515f6e0f8c51dc0f8e4696391e6f1ff30379559f8365fb0df7" dependencies = [ - "rustix 0.37.23", + "rustix", "windows-sys 0.48.0", ] @@ -2235,42 +2298,33 @@ checksum = "3369f5ac52d5eb6ab48c6b4ffdc8efbcad6b89c765749064ba298f2c68a16a76" [[package]] name = "thiserror" -version = "1.0.43" +version = "1.0.56" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a35fc5b8971143ca348fa6df4f024d4d55264f3468c71ad1c2f365b0a4d58c42" +checksum = "d54378c645627613241d077a3a79db965db602882668f9136ac42af9ecb730ad" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.43" +version = "1.0.56" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "463fe12d7993d3b327787537ce8dd4dfa058de32fc2b195ef3cde03dc4771e8f" +checksum = "fa0faa943b50f3db30a20aa7e265dbc66076993efed8463e8de414e5d06d3471" dependencies = [ "proc-macro2", "quote", - "syn 2.0.25", + "syn 2.0.48", ] [[package]] name = "time" -version = "0.1.45" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b797afad3f312d1c66a56d11d0316f916356d11bd158fbc6ca6389ff6bf805a" -dependencies = [ - "libc", - "wasi 0.10.0+wasi-snapshot-preview1", - "winapi", -] - -[[package]] -name = "time" -version = "0.3.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59e399c068f43a5d116fedaf73b203fa4f9c519f17e2b34f63221d3792f81446" +checksum = "f657ba42c3f86e7680e53c8cd3af8abbe56b5491790b46e22e19c0d57463583e" dependencies = [ + "deranged", "itoa", + "powerfmt", "serde", "time-core", "time-macros", @@ -2278,15 +2332,15 @@ dependencies = [ [[package]] name = "time-core" -version = "0.1.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7300fbefb4dadc1af235a9cef3737cea692a9d97e1b9cbcd4ebdae6f8868e6fb" +checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" [[package]] name = "time-macros" -version = "0.2.10" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96ba15a897f3c86766b757e5ac7221554c6750054d74d5b28844fce5fb36a6c4" +checksum = "26197e33420244aeb70c3e8c78376ca46571bc4e701e4791c2cd9f57dcb3a43f" dependencies = [ "time-core", ] @@ -2315,12 +2369,14 @@ dependencies = [ "bytes", "clap", "clap_complete", - "crossterm 0.26.1", + "crossterm", + "ctrlc", "flexi_logger", "indicatif", "log", "mockito", "predicates", + "ratatui", "reqwest", "rpassword", "serde", @@ -2330,17 +2386,16 @@ dependencies = [ "terminal_size", "tmc-langs", "toml", - "tui", "url", "uuid", ] [[package]] name = "tmc-langs" -version = "0.33.0" -source = "git+https://github.com/rage/tmc-langs-rust?tag=0.33.0#136ce62cf7f0ed09f43d755d3d9b496a8219ffaf" +version = "0.34.0" +source = "git+https://github.com/rage/tmc-langs-rust?tag=0.36.0#3f7ab2d40d32a71bfcdbb7810f43e695a00089ff" dependencies = [ - "base64 0.21.2", + "base64 0.21.6", "blake3", "chrono", "dirs", @@ -2372,13 +2427,13 @@ dependencies = [ "uuid", "walkdir", "zip", - "zstd 0.12.3+zstd.1.5.2", + "zstd 0.12.4", ] [[package]] name = "tmc-langs-csharp" -version = "0.33.0" -source = "git+https://github.com/rage/tmc-langs-rust?tag=0.33.0#136ce62cf7f0ed09f43d755d3d9b496a8219ffaf" +version = "0.34.0" +source = "git+https://github.com/rage/tmc-langs-rust?tag=0.36.0#3f7ab2d40d32a71bfcdbb7810f43e695a00089ff" dependencies = [ "dirs", "log", @@ -2393,8 +2448,8 @@ dependencies = [ [[package]] name = "tmc-langs-framework" -version = "0.33.0" -source = "git+https://github.com/rage/tmc-langs-rust?tag=0.33.0#136ce62cf7f0ed09f43d755d3d9b496a8219ffaf" +version = "0.34.0" +source = "git+https://github.com/rage/tmc-langs-rust?tag=0.36.0#3f7ab2d40d32a71bfcdbb7810f43e695a00089ff" dependencies = [ "fd-lock", "isolang", @@ -2412,13 +2467,13 @@ dependencies = [ "tmc-langs-util", "walkdir", "zip", - "zstd 0.12.3+zstd.1.5.2", + "zstd 0.12.4", ] [[package]] name = "tmc-langs-java" -version = "0.33.0" -source = "git+https://github.com/rage/tmc-langs-rust?tag=0.33.0#136ce62cf7f0ed09f43d755d3d9b496a8219ffaf" +version = "0.34.0" +source = "git+https://github.com/rage/tmc-langs-rust?tag=0.36.0#3f7ab2d40d32a71bfcdbb7810f43e695a00089ff" dependencies = [ "dirs", "flate2", @@ -2437,8 +2492,8 @@ dependencies = [ [[package]] name = "tmc-langs-make" -version = "0.33.0" -source = "git+https://github.com/rage/tmc-langs-rust?tag=0.33.0#136ce62cf7f0ed09f43d755d3d9b496a8219ffaf" +version = "0.34.0" +source = "git+https://github.com/rage/tmc-langs-rust?tag=0.36.0#3f7ab2d40d32a71bfcdbb7810f43e695a00089ff" dependencies = [ "log", "once_cell", @@ -2454,8 +2509,8 @@ dependencies = [ [[package]] name = "tmc-langs-notests" -version = "0.33.0" -source = "git+https://github.com/rage/tmc-langs-rust?tag=0.33.0#136ce62cf7f0ed09f43d755d3d9b496a8219ffaf" +version = "0.34.0" +source = "git+https://github.com/rage/tmc-langs-rust?tag=0.36.0#3f7ab2d40d32a71bfcdbb7810f43e695a00089ff" dependencies = [ "log", "tmc-langs-framework", @@ -2465,8 +2520,8 @@ dependencies = [ [[package]] name = "tmc-langs-plugins" -version = "0.33.0" -source = "git+https://github.com/rage/tmc-langs-rust?tag=0.33.0#136ce62cf7f0ed09f43d755d3d9b496a8219ffaf" +version = "0.34.0" +source = "git+https://github.com/rage/tmc-langs-rust?tag=0.36.0#3f7ab2d40d32a71bfcdbb7810f43e695a00089ff" dependencies = [ "impl-enum", "log", @@ -2482,13 +2537,13 @@ dependencies = [ "tmc-langs-util", "walkdir", "zip", - "zstd 0.12.3+zstd.1.5.2", + "zstd 0.12.4", ] [[package]] name = "tmc-langs-python3" -version = "0.33.0" -source = "git+https://github.com/rage/tmc-langs-rust?tag=0.33.0#136ce62cf7f0ed09f43d755d3d9b496a8219ffaf" +version = "0.34.0" +source = "git+https://github.com/rage/tmc-langs-rust?tag=0.36.0#3f7ab2d40d32a71bfcdbb7810f43e695a00089ff" dependencies = [ "dunce", "hex", @@ -2508,8 +2563,8 @@ dependencies = [ [[package]] name = "tmc-langs-r" -version = "0.33.0" -source = "git+https://github.com/rage/tmc-langs-rust?tag=0.33.0#136ce62cf7f0ed09f43d755d3d9b496a8219ffaf" +version = "0.34.0" +source = "git+https://github.com/rage/tmc-langs-rust?tag=0.36.0#3f7ab2d40d32a71bfcdbb7810f43e695a00089ff" dependencies = [ "log", "serde", @@ -2522,8 +2577,8 @@ dependencies = [ [[package]] name = "tmc-langs-util" -version = "0.33.0" -source = "git+https://github.com/rage/tmc-langs-rust?tag=0.33.0#136ce62cf7f0ed09f43d755d3d9b496a8219ffaf" +version = "0.34.0" +source = "git+https://github.com/rage/tmc-langs-rust?tag=0.36.0#3f7ab2d40d32a71bfcdbb7810f43e695a00089ff" dependencies = [ "dunce", "fd-lock", @@ -2544,8 +2599,8 @@ dependencies = [ [[package]] name = "tmc-mooc-client" -version = "0.33.0" -source = "git+https://github.com/rage/tmc-langs-rust?tag=0.33.0#136ce62cf7f0ed09f43d755d3d9b496a8219ffaf" +version = "0.34.0" +source = "git+https://github.com/rage/tmc-langs-rust?tag=0.36.0#3f7ab2d40d32a71bfcdbb7810f43e695a00089ff" dependencies = [ "bytes", "chrono", @@ -2562,8 +2617,8 @@ dependencies = [ [[package]] name = "tmc-testmycode-client" -version = "0.33.0" -source = "git+https://github.com/rage/tmc-langs-rust?tag=0.33.0#136ce62cf7f0ed09f43d755d3d9b496a8219ffaf" +version = "0.34.0" +source = "git+https://github.com/rage/tmc-langs-rust?tag=0.36.0#3f7ab2d40d32a71bfcdbb7810f43e695a00089ff" dependencies = [ "chrono", "dirs", @@ -2587,11 +2642,10 @@ dependencies = [ [[package]] name = "tokio" -version = "1.29.1" +version = "1.35.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "532826ff75199d5833b9d2c5fe410f29235e25704ee5f0ef599fb51c21f4a4da" +checksum = "c89b4efa943be685f629b149f53829423f8f5531ea21249408e8e2f8671ec104" dependencies = [ - "autocfg", "backtrace", "bytes", "libc", @@ -2607,13 +2661,13 @@ dependencies = [ [[package]] name = "tokio-macros" -version = "2.1.0" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e" +checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.25", + "syn 2.0.48", ] [[package]] @@ -2628,9 +2682,9 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.7.8" +version = "0.7.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "806fe8c2c87eccc8b3267cbae29ed3ab2d0bd37fca70ab622e46aaa9375ddb7d" +checksum = "5419f34732d9eb6ee4c3578b7989078579b7f039cbbb9ca2c4da015749371e15" dependencies = [ "bytes", "futures-core", @@ -2642,9 +2696,9 @@ dependencies = [ [[package]] name = "toml" -version = "0.7.6" +version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c17e963a819c331dcacd7ab957d80bc2b9a9c1e71c804826d2f283dd65306542" +checksum = "a1a195ec8c9da26928f773888e0742ca3ca1040c6cd859c919c9f59c1954ab35" dependencies = [ "serde", "serde_spanned", @@ -2654,20 +2708,20 @@ dependencies = [ [[package]] name = "toml_datetime" -version = "0.6.3" +version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7cda73e2f1397b1262d6dfdcef8aafae14d1de7748d66822d3bfeeb6d03e5e4b" +checksum = "3550f4e9685620ac18a50ed434eb3aec30db8ba93b0287467bca5826ea25baf1" dependencies = [ "serde", ] [[package]] name = "toml_edit" -version = "0.19.12" +version = "0.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c500344a19072298cd05a7224b3c0c629348b78692bf48466c5238656e315a78" +checksum = "d34d383cd00a163b4a5b85053df514d45bc330f6de7737edfe0a93311d1eaa03" dependencies = [ - "indexmap 2.0.0", + "indexmap", "serde", "serde_spanned", "toml_datetime", @@ -2682,42 +2736,28 @@ checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" [[package]] name = "tracing" -version = "0.1.37" +version = "0.1.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8" +checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" dependencies = [ - "cfg-if", "pin-project-lite", "tracing-core", ] [[package]] name = "tracing-core" -version = "0.1.31" +version = "0.1.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0955b8137a1df6f1a2e9a37d8a6656291ff0297c1a97c24e0d8425fe2312f79a" +checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" dependencies = [ "once_cell", ] [[package]] name = "try-lock" -version = "0.2.4" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed" - -[[package]] -name = "tui" -version = "0.19.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ccdd26cbd674007e649a272da4475fb666d3aa0ad0531da7136db6fab0e5bad1" -dependencies = [ - "bitflags 1.3.2", - "cassowary", - "crossterm 0.25.0", - "unicode-segmentation", - "unicode-width", -] +checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" [[package]] name = "type-map" @@ -2730,30 +2770,30 @@ dependencies = [ [[package]] name = "typenum" -version = "1.16.0" +version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" +checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" [[package]] name = "unicase" -version = "2.6.0" +version = "2.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50f37be617794602aabbeee0be4f259dc1778fabe05e2d67ee8f79326d5cb4f6" +checksum = "f7d2d4dafb69621809a81864c9c1b864479e1235c0dd4e199924b9742439ed89" dependencies = [ "version_check", ] [[package]] name = "unicode-bidi" -version = "0.3.13" +version = "0.3.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460" +checksum = "6f2528f27a9eb2b21e69c95319b30bd0efd85d09c379741b0f78ea1d86be2416" [[package]] name = "unicode-ident" -version = "1.0.10" +version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22049a19f4a68748a168c0fc439f9516686aa045927ff767eca0a85101fb6e73" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" [[package]] name = "unicode-normalization" @@ -2772,30 +2812,30 @@ checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36" [[package]] name = "unicode-width" -version = "0.1.10" +version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b" +checksum = "e51733f11c9c4f72aa0c160008246859e340b00807569a0da0e7a1079b27ba85" [[package]] name = "unsafe-libyaml" -version = "0.2.8" +version = "0.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1865806a559042e51ab5414598446a5871b561d21b6764f2eabb0dd481d880a6" +checksum = "ab4c90930b95a82d00dc9e9ac071b4991924390d46cbd0dfe566148667605e4b" [[package]] name = "untrusted" -version = "0.7.1" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" [[package]] name = "url" -version = "2.4.0" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50bff7831e19200a85b17131d085c25d7811bc4e186efdaf54bbd132994a88cb" +checksum = "31e6302e3bb753d46e83516cae55ae196fc0c309407cf11ab35cc51a4c2a4633" dependencies = [ "form_urlencoded", - "idna 0.4.0", + "idna 0.5.0", "percent-encoding", "serde", ] @@ -2808,9 +2848,9 @@ checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" [[package]] name = "uuid" -version = "1.4.0" +version = "1.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d023da39d1fde5a8a3fe1f3e01ca9632ada0a63e9797de55a879d6e2236277be" +checksum = "5e395fcf16a7a3d8127ec99782007af141946b4795001f876d54fb0d55978560" dependencies = [ "getrandom", "serde", @@ -2833,9 +2873,9 @@ dependencies = [ [[package]] name = "walkdir" -version = "2.3.3" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36df944cda56c7d8d8b7496af378e6b16de9284591917d307c9b4d313c44e698" +checksum = "d71d857dc86794ca4c280d616f7da00d2dbfd8cd788846559a6813e6aa4b54ee" dependencies = [ "same-file", "winapi-util", @@ -2850,12 +2890,6 @@ dependencies = [ "try-lock", ] -[[package]] -name = "wasi" -version = "0.10.0+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f" - [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" @@ -2864,9 +2898,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.87" +version = "0.2.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7706a72ab36d8cb1f80ffbf0e071533974a60d0a308d01a5d0375bf60499a342" +checksum = "0ed0d4f68a3015cc185aff4db9506a015f4b96f95303897bfa23f846db54064e" dependencies = [ "cfg-if", "wasm-bindgen-macro", @@ -2874,24 +2908,24 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.87" +version = "0.2.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ef2b6d3c510e9625e5fe6f509ab07d66a760f0885d858736483c32ed7809abd" +checksum = "1b56f625e64f3a1084ded111c4d5f477df9f8c92df113852fa5a374dbda78826" dependencies = [ "bumpalo", "log", "once_cell", "proc-macro2", "quote", - "syn 2.0.25", + "syn 2.0.48", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-futures" -version = "0.4.37" +version = "0.4.39" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c02dbc21516f9f1f04f187958890d7e6026df8d16540b7ad9492bc34a67cea03" +checksum = "ac36a15a220124ac510204aec1c3e5db8a22ab06fd6706d881dc6149f8ed9a12" dependencies = [ "cfg-if", "js-sys", @@ -2901,9 +2935,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.87" +version = "0.2.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dee495e55982a3bd48105a7b947fd2a9b4a8ae3010041b9e0faab3f9cd028f1d" +checksum = "0162dbf37223cd2afce98f3d0785506dcb8d266223983e4b5b525859e6e182b2" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -2911,51 +2945,38 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.87" +version = "0.2.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b" +checksum = "f0eb82fcb7930ae6219a7ecfd55b217f5f0893484b7a13022ebb2b2bf20b5283" dependencies = [ "proc-macro2", "quote", - "syn 2.0.25", + "syn 2.0.48", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.87" +version = "0.2.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca6ad05a4870b2bf5fe995117d3728437bd27d7cd5f06f13c17443ef369775a1" +checksum = "7ab9b36309365056cd639da3134bf87fa8f3d86008abf99e612384a6eecd459f" [[package]] name = "web-sys" -version = "0.3.64" +version = "0.3.66" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b85cbef8c220a6abc02aefd892dfc0fc23afb1c6a426316ec33253a3877249b" +checksum = "50c24a44ec86bb68fbecd1b3efed7e85ea5621b39b35ef2766b66cd984f8010f" dependencies = [ "js-sys", "wasm-bindgen", ] -[[package]] -name = "webpki" -version = "0.22.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f095d78192e208183081cc07bc5515ef55216397af48b873e5edcd72637fa1bd" -dependencies = [ - "ring", - "untrusted", -] - [[package]] name = "webpki-roots" -version = "0.22.6" +version = "0.25.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6c71e40d7d2c34a5106301fb632274ca37242cd0c9d3e64dbece371a40a2d87" -dependencies = [ - "webpki", -] +checksum = "1778a42e8b3b90bff8d0f5032bf22250792889a5cdc752aa0020c84abe3aaf10" [[package]] name = "winapi" @@ -2975,9 +2996,9 @@ checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] name = "winapi-util" -version = "0.1.5" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" +checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596" dependencies = [ "winapi", ] @@ -2989,12 +3010,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] -name = "windows" -version = "0.48.0" +name = "windows-core" +version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f" +checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" dependencies = [ - "windows-targets 0.48.1", + "windows-targets 0.52.0", ] [[package]] @@ -3012,7 +3033,16 @@ version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" dependencies = [ - "windows-targets 0.48.1", + "windows-targets 0.48.5", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets 0.52.0", ] [[package]] @@ -3032,17 +3062,32 @@ dependencies = [ [[package]] name = "windows-targets" -version = "0.48.1" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05d4b17490f70499f20b9e791dcf6a299785ce8af4d709018206dc5b4953e95f" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" dependencies = [ - "windows_aarch64_gnullvm 0.48.0", - "windows_aarch64_msvc 0.48.0", - "windows_i686_gnu 0.48.0", - "windows_i686_msvc 0.48.0", - "windows_x86_64_gnu 0.48.0", - "windows_x86_64_gnullvm 0.48.0", - "windows_x86_64_msvc 0.48.0", + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", +] + +[[package]] +name = "windows-targets" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd" +dependencies = [ + "windows_aarch64_gnullvm 0.52.0", + "windows_aarch64_msvc 0.52.0", + "windows_i686_gnu 0.52.0", + "windows_i686_msvc 0.52.0", + "windows_x86_64_gnu 0.52.0", + "windows_x86_64_gnullvm 0.52.0", + "windows_x86_64_msvc 0.52.0", ] [[package]] @@ -3053,9 +3098,15 @@ checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" [[package]] name = "windows_aarch64_gnullvm" -version = "0.48.0" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" +checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea" [[package]] name = "windows_aarch64_msvc" @@ -3065,9 +3116,15 @@ checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" [[package]] name = "windows_aarch64_msvc" -version = "0.48.0" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef" [[package]] name = "windows_i686_gnu" @@ -3077,9 +3134,15 @@ checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" [[package]] name = "windows_i686_gnu" -version = "0.48.0" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" +checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313" [[package]] name = "windows_i686_msvc" @@ -3089,9 +3152,15 @@ checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" [[package]] name = "windows_i686_msvc" -version = "0.48.0" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a" [[package]] name = "windows_x86_64_gnu" @@ -3101,9 +3170,15 @@ checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" [[package]] name = "windows_x86_64_gnu" -version = "0.48.0" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd" [[package]] name = "windows_x86_64_gnullvm" @@ -3113,9 +3188,15 @@ checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" [[package]] name = "windows_x86_64_gnullvm" -version = "0.48.0" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" +checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e" [[package]] name = "windows_x86_64_msvc" @@ -3125,42 +3206,71 @@ checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" [[package]] name = "windows_x86_64_msvc" -version = "0.48.0" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" +checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" [[package]] name = "winnow" -version = "0.4.9" +version = "0.5.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81a2094c43cc94775293eaa0e499fbc30048a6d824ac82c0351a8c0bf9112529" +checksum = "b7520bbdec7211caa7c4e682eb1fbe07abe20cee6756b6e00f537c82c11816aa" dependencies = [ "memchr", ] [[package]] name = "winreg" -version = "0.10.1" +version = "0.50.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80d0f4e272c85def139476380b12f9ac60926689dd2e01d4923222f40580869d" +checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1" dependencies = [ - "winapi", + "cfg-if", + "windows-sys 0.48.0", ] [[package]] name = "xattr" -version = "0.2.3" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d1526bbe5aaeb5eb06885f4d987bcdfa5e23187055de9b83fe00156a821fabc" +checksum = "914566e6413e7fa959cc394fb30e563ba80f3541fbd40816d4c05a0fc3f2a0f1" dependencies = [ "libc", + "linux-raw-sys", + "rustix", ] [[package]] name = "xml-rs" -version = "0.8.15" +version = "0.8.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a56c84a8ccd4258aed21c92f70c0f6dea75356b6892ae27c24139da456f9336" +checksum = "0fcb9cbac069e033553e8bb871be2fbdffcab578eb25bd0f7c508cedc6dcd75a" + +[[package]] +name = "zerocopy" +version = "0.7.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74d4d3961e53fa4c9a25a8637fc2bfaf2595b3d3ae34875568a5cf64787716be" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.48", +] [[package]] name = "zip" @@ -3178,7 +3288,7 @@ dependencies = [ "hmac", "pbkdf2", "sha1", - "time 0.3.23", + "time", "zstd 0.11.2+zstd.1.5.2", ] @@ -3193,11 +3303,11 @@ dependencies = [ [[package]] name = "zstd" -version = "0.12.3+zstd.1.5.2" +version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76eea132fb024e0e13fd9c2f5d5d595d8a967aa72382ac2f9d39fcc95afd0806" +checksum = "1a27595e173641171fc74a1232b7b1c7a7cb6e18222c11e9dfb9888fa424c53c" dependencies = [ - "zstd-safe 6.0.5+zstd.1.5.4", + "zstd-safe 6.0.6", ] [[package]] @@ -3212,9 +3322,9 @@ dependencies = [ [[package]] name = "zstd-safe" -version = "6.0.5+zstd.1.5.4" +version = "6.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d56d9e60b4b1758206c238a10165fbcae3ca37b01744e394c463463f6529d23b" +checksum = "ee98ffd0b48ee95e6c5168188e44a54550b1564d9d530ee21d5f0eaed1069581" dependencies = [ "libc", "zstd-sys", @@ -3222,11 +3332,10 @@ dependencies = [ [[package]] name = "zstd-sys" -version = "2.0.8+zstd.1.5.5" +version = "2.0.9+zstd.1.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5556e6ee25d32df2586c098bbfa278803692a20d0ab9565e049480d52707ec8c" +checksum = "9e16efa8a874a0481a574084d34cc26fdb3b99627480f785888deb6386506656" dependencies = [ "cc", - "libc", "pkg-config", ] diff --git a/Cargo.toml b/Cargo.toml index 244cae7..e677215 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,10 +19,12 @@ anyhow = { version = "1.0.56", features = ["backtrace"] } bytes = "1.4.0" clap = { version = "4.0.7", features = ["derive"] } clap_complete = "4.0.2" -crossterm = "0.26.0" -flexi_logger = "0.25.3" +crossterm = "0.27.0" +ctrlc = "3.4.2" +flexi_logger = "0.27.3" indicatif = "0.17.1" log = "0.4.17" +ratatui = { version = "0.25.0", default-features = false, features = ['crossterm'] } reqwest = { version = "0.11.9", default-features = false, features = [ "blocking", "json", @@ -33,10 +35,9 @@ rpassword = "7.0.0" serde = "1.0.136" serde_json = "1.0.79" termcolor = "1.1.3" -terminal_size = "0.2.1" -tmc-langs = { git = "https://github.com/rage/tmc-langs-rust", tag = "0.33.0" } -toml = "0.7.2" -tui = { version = "0.19.0", default-features = false, features = ['crossterm'] } +terminal_size = "0.3.0" +tmc-langs = { git = "https://github.com/rage/tmc-langs-rust", tag = "0.36.0" } +toml = "0.8.8" url = "2.2.2" uuid = { version = "1.4.0", features = ["v4"] } diff --git a/src/cli.rs b/src/cli.rs index e015885..286f016 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -1,5 +1,4 @@ use clap::{Parser, Subcommand, ValueEnum}; -use std::path::PathBuf; #[derive(Parser, Debug)] #[command( @@ -28,11 +27,11 @@ pub struct Cli { #[derive(Subcommand, Debug)] pub enum Command { - // tmc commands - /// List the available courses. - Courses, /// Download exercises for a course. Download { + /// If set, this organization is pre-selected for downloading TMC exercises. + #[arg(short, long, value_name = "TMC organization")] + organization: Option, /// If set, the exercises of this course are downloaded. If not set, the selection is done from an interactive menu. #[arg(short, long, value_name = "course name")] course: Option, @@ -40,21 +39,10 @@ pub enum Command { #[arg(short = 'd', long)] currentdir: bool, }, - /// List the exercises for a specific course. - Exercises { - /// If set, the exercises of this course are listed. If not set, the selection is done from an interactive menu. - course: Option, - }, /// Login to TMC server. Login, /// Logout from TMC server. Logout, - /// Change organization. - Organization { - /// Initiates the non-interactive mode. - #[arg(short, long)] - non_interactive: bool, - }, /// Submit exercise to TMC pastebin. Paste { exercise: Option }, /// Submit exercises to TMC server. @@ -68,33 +56,6 @@ pub enum Command { currentdir: bool, }, - // MOOC commands - /// Currently enrolled courses.mooc.fi courses. - MoocCourses, - /// Active exercises of the selected course. - MoocCourseExercises { - /// If set, the exercises of this course are listed. If not set, the selection is done from an interactive menu. - course: Option, - }, - /// Downloads active exercises for the selected course. - MoocDownloadExercises { - /// If set, the exercises of this course are downloaded. If not set, the selection is done from an interactive menu. - course: Option, - /// If set, exercises are downloaded to the current working directory. - #[arg(short = 'd', long)] - currentdir: bool, - }, - /// Updates local exercises for the selected course. - MoocUpdateExercises { - /// If set, the exercises of this course are downloaded. If not set, the selection is done from an interactive menu. - course: Option, - }, - /// Submits an exercise. - MoocSubmitExercise { - /// If set, the exercise at this path is submitted. If not set, the selection is done from an interactive menu. - path: Option, - }, - // hidden commands /// Finishes the autoupdater. Administator rights needed. #[clap(hide = true)] @@ -117,12 +78,6 @@ pub enum Command { GenerateCompletions { shell: ShellArg }, } -impl Command { - pub fn requires_organization_set(&self) -> bool { - matches!(self, Command::Download { .. } | Command::Courses { .. }) - } -} - #[derive(Debug, Clone, Copy, ValueEnum)] pub enum ShellArg { Bash, diff --git a/src/client.rs b/src/client.rs index 53f8c87..d77d92e 100644 --- a/src/client.rs +++ b/src/client.rs @@ -6,9 +6,7 @@ use std::path::Path; use tmc_langs::{ mooc::{self, ExerciseTaskSubmissionResult, ExerciseTaskSubmissionStatus, MoocClient}, tmc::{ - response::{ - Course, CourseDetails, CourseExercise, NewSubmission, Organization, SubmissionFinished, - }, + response::{Course, CourseExercise, NewSubmission, Organization, SubmissionFinished}, TestMyCodeClient, TestMyCodeClientError, Token, }, Credentials, DownloadOrUpdateCourseExercisesResult, DownloadResult, LangsError, Language, @@ -182,13 +180,12 @@ impl Client { course_list.push(Course { name: course.name, id: course.id, - - title: "".to_string(), + title: course.title, description: None, - details_url: "".to_string(), - unlock_url: "".to_string(), - reviews_url: "".to_string(), - comet_url: "".to_string(), + details_url: course.details_url, + unlock_url: course.unlock_url, + reviews_url: course.reviews_url, + comet_url: course.comet_url, spyware_urls: vec![], }); } @@ -332,34 +329,8 @@ impl Client { ) } - pub fn get_course_details( - &self, - course_id: u32, - ) -> Result { - if self.test_mode { - let course = Course { - id: 0, - name: "".to_string(), - title: "".to_string(), - description: None, - details_url: "".to_string(), - unlock_url: "".to_string(), - reviews_url: "".to_string(), - comet_url: "".to_string(), - spyware_urls: vec![], - }; - Ok(CourseDetails { - course, - unlockables: vec![], - exercises: vec![], - }) - } else { - self.tmc_client.get_course_details(course_id) - } - } - // mooc commands - pub fn mooc_courses(&self) -> anyhow::Result> { + pub fn enrolled_mooc_courses(&self) -> anyhow::Result> { let courses = self.mooc_client.course_instances()?; Ok(courses) } diff --git a/src/commands.rs b/src/commands.rs index ecc0784..59c9e7b 100644 --- a/src/commands.rs +++ b/src/commands.rs @@ -1,11 +1,8 @@ -mod courses; mod download; -mod exercises; mod generate_completions; mod login; mod logout; mod mooc; -mod organization; mod paste; mod submit; mod test; @@ -22,104 +19,51 @@ use anyhow::Context; use std::env; pub fn handle(cli: Cli, io: &mut Io, mut config: TmcCliConfig) -> anyhow::Result<()> { + let default_tmc_url = "https://tmc.mooc.fi"; + let default_mooc_url = "https://courses.mooc.fi"; + let tmc_root_url = match env::var("TMC_LANGS_TMC_ROOT_URL") { Ok(url) => url .parse() .with_context(|| format!("Failed to parse TMC_LANGS_TMC_ROOT_URL ({url}) as a URL"))?, - Err(_) => "https://tmc.mooc.fi".parse().expect("known to work"), + Err(_) => default_tmc_url.parse().expect("known to work"), }; - let mooc_root_url = env::var("TMC_LANGS_MOOC_ROOT_URL") - .unwrap_or_else(|_| "https://courses.mooc.fi".to_string()); + let mooc_root_url = + env::var("TMC_LANGS_MOOC_ROOT_URL").unwrap_or_else(|_| default_mooc_url.to_string()); let mut client = Client::new(tmc_root_url, mooc_root_url, cli.testmode)?; - let require_logged_out = |client: &mut Client| { - let exists = client.load_login(&config).is_ok(); - if exists { - anyhow::bail!("Already logged in. Please logout first with 'tmc logout'"); - } - anyhow::Ok(()) - }; - let require_logged_in = |client: &mut Client| { - let exists = client.load_login(&config).is_ok(); - if !exists { - anyhow::bail!("No login found. Login to use this command with 'tmc login'"); - } - anyhow::Ok(()) - }; - let require_org = || { - config.get_organization().ok_or_else(|| anyhow::anyhow!("No organization selected. You can select an organization with the `organization` command.")) - }; - match cli.subcommand { - // tmc commands Command::Login => { - require_logged_out(&mut client)?; login::login(io, &mut client, &mut config)?; } - Command::Download { course, currentdir } => { - require_logged_in(&mut client)?; - let org = require_org()?; + Command::Logout => { + logout::logout(io, &mut client, &mut config)?; + } + Command::Download { + organization, + course, + currentdir, + } => { download::download_or_update( io, &mut client, + &mut config, + organization, course.as_deref(), currentdir, - &config, - org, )?; } Command::Update { currentdir } => { - require_logged_in(&mut client)?; - update::update(io, &mut client, currentdir, &config)?; - } - Command::Organization { non_interactive } => { - require_logged_in(&mut client)?; - let interactive_mode = !non_interactive; - organization::organization(io, &mut client, interactive_mode, &mut config)?; - } - Command::Courses => { - require_logged_in(&mut client)?; - let org = require_org()?; - courses::list_courses(io, &mut client, org)?; - } - Command::Submit { exercise } => { - require_logged_in(&mut client)?; - submit::submit(io, &mut client, exercise.as_deref(), &config)?; - } - Command::Exercises { course } => { - require_logged_in(&mut client)?; - let org = require_org()?; - exercises::list_exercises(io, &mut client, course.as_deref(), org)? + update::update(io, &mut client, &mut config, currentdir)?; } Command::Test { exercise } => { - test::test(io, exercise.as_deref(), &config)?; + test::test(io, &config, exercise.as_deref())?; } Command::Paste { exercise } => { - require_logged_in(&mut client)?; - paste::paste(io, &mut client, exercise.as_deref(), &config)?; + paste::paste(io, &mut client, &mut config, exercise.as_deref())?; } - Command::Logout => { - require_logged_in(&mut client)?; - logout::logout(io, &mut client, &mut config)?; - } - - // mooc commands - Command::MoocCourses => mooc::courses::run(io, &mut client)?, - Command::MoocCourseExercises { course } => { - mooc::course_exercises::run(io, &mut client, course.as_deref())? - } - Command::MoocDownloadExercises { course, currentdir } => mooc::download_exercises::run( - io, - &mut client, - course.as_deref(), - currentdir, - &mut config, - )?, - Command::MoocUpdateExercises { course } => { - mooc::update_exercises::run(io, &mut client, course.as_deref(), &mut config)? - } - Command::MoocSubmitExercise { path } => { - mooc::submit_exercise::run(io, &mut client, path.as_deref(), &config)? + Command::Submit { exercise } => { + submit::submit(io, &mut client, &mut config, exercise.as_deref())?; } // hidden commands @@ -132,11 +76,10 @@ pub fn handle(cli: Cli, io: &mut Io, mut config: TmcCliConfig) -> anyhow::Result crate::updater::cleartemp()?; } Command::Elevateddownload => { - let org = require_org()?; - download::elevated_download(io, &mut client, &config, org)?; + download::elevated_download(io, &mut client, &mut config)?; } Command::Elevatedupdate => { - update::elevated_update(io, &mut client, &config)?; + update::elevated_update(io, &mut client, &mut config)?; } Command::GenerateCompletions { shell } => { generate_completions::generate(shell); @@ -144,3 +87,8 @@ pub fn handle(cli: Cli, io: &mut Io, mut config: TmcCliConfig) -> anyhow::Result } Ok(()) } + +pub enum Platform { + Mooc, + Tmc, +} diff --git a/src/commands/courses.rs b/src/commands/courses.rs deleted file mode 100644 index cd61b3c..0000000 --- a/src/commands/courses.rs +++ /dev/null @@ -1,109 +0,0 @@ -use crate::{ - client::Client, - io::{Io, PrintColor}, -}; - -/// Lists available courses from clients organization -pub fn list_courses(io: &mut Io, client: &mut Client, org: &str) -> anyhow::Result<()> { - let mut course_list = client.list_courses(org)?; - course_list.sort_unstable_by(|l, r| l.name.cmp(&r.name)); - io.println("", PrintColor::Normal)?; - for course in course_list { - io.println(&course.name, PrintColor::Normal)?; - } - Ok(()) -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::test_helper::{self, TestSetup}; - use mockito::{Matcher, Mock, Server, ServerGuard}; - - fn mock_server() -> (ServerGuard, Vec) { - let mut server = Server::new(); - let mut mocks = Vec::new(); - mocks.push( - server - .mock("GET", "/api/v8/core/org/test/courses") - .match_query(Matcher::Any) - .with_body( - serde_json::json!([ - { - "id": 1, - "name": "name", - "title": "title", - "details_url": "example.com", - "unlock_url": "example.com", - "reviews_url": "example.com", - "comet_url": "example.com", - "spyware_urls": ["example.com"], - }, - { - "id": 1, - "name": "mooc-tutustumiskurssi", - "title": "title", - "details_url": "example.com", - "unlock_url": "example.com", - "reviews_url": "example.com", - "comet_url": "example.com", - "spyware_urls": ["example.com"], - }, - ]) - .to_string(), - ) - .create(), - ); - mocks.push( - server - .mock("GET", "/api/v8/courses/1/exercises") - .match_query(Matcher::Any) - .with_body( - serde_json::json!([ - { - "id": 1, - "available_points": [], - "awarded_points": [], - "name": "part01-01_example_exercise", - "disabled": false, - "unlocked": true, - }, - { - "id": 2, - "available_points": [], - "awarded_points": [], - "name": "part02-03_example_valid", - "disabled": false, - "unlocked": true, - }, - ]) - .to_string(), - ) - .create(), - ); - (server, mocks) - } - - #[test] - fn list_courses_with_client_test() { - test_helper::logging(); - let (server, _mocks) = mock_server(); - let (mut input, mut output) = test_helper::input_output(); - let TestSetup { - mut io, mut client, .. - } = test_helper::tmc_setup(&mut input, &mut output, &server); - client.set_tmc_token(test_helper::tmc_token()); - - list_courses(&mut io, &mut client, "test").unwrap(); - - let output = String::from_utf8(output.into_inner()).unwrap(); - let output = output.lines().collect::>(); - assert!(output[0].eq(""), "first line should be empty"); - assert!( - output[1].eq("mooc-tutustumiskurssi"), - "Expected 'mooc-tutustumiskurssi', got {}", - output[1] - ); - assert!(output[2].eq("name"), "Expected 'name', got '{}'", output[2]); - } -} diff --git a/src/commands/download.rs b/src/commands/download.rs index cbec077..4c3c902 100644 --- a/src/commands/download.rs +++ b/src/commands/download.rs @@ -1,4 +1,4 @@ -use super::util; +use super::{mooc, util, Platform}; use crate::{ client::Client, config::TmcCliConfig, @@ -7,17 +7,38 @@ use crate::{ progress_reporting::ProgressBarManager, }; use anyhow::Context; -use std::{path::Path, process::Command}; +use std::{io::prelude::*, path::Path, process::Command}; use tmc_langs::{ tmc::{response::Course, ClientUpdateData}, DownloadResult, }; +pub fn download_or_update( + io: &mut Io, + client: &mut Client, + config: &mut TmcCliConfig, + org: Option, + course: Option<&str>, + current_dir: bool, +) -> anyhow::Result<()> { + util::ensure_logged_in(client, io, config)?; + match util::select_courses_or_tmc()? { + Platform::Mooc => { + mooc::download_exercises::run(io, client, course, current_dir, config)?; + } + Platform::Tmc => { + let org = util::get_or_select_organization(org, client, io)?; + tmc_download_or_update(io, client, course, current_dir, &config, &org)?; + } + }; + Ok(()) +} + // Downloads course exercises // course_name as None will trigger interactive menu for selecting a course // currentdir determines if course should be downloaded to current directory or central project directory // Will run in privileged stage if needed on Windows. -pub fn download_or_update( +fn tmc_download_or_update( io: &mut Io, client: &mut Client, course_name: Option<&str>, @@ -87,7 +108,7 @@ pub fn download_or_update( } } -pub fn download_exercises( +fn download_exercises( projects_dir: &Path, client: &mut Client, course: &Course, @@ -173,12 +194,29 @@ pub fn download_exercises( } pub fn elevated_download( + io: &mut Io, + client: &mut Client, + config: &mut TmcCliConfig, +) -> anyhow::Result<()> { + util::ensure_logged_in(client, io, config)?; + match util::select_courses_or_tmc()? { + Platform::Mooc => { + todo!() + } + Platform::Tmc => { + let org = util::get_or_select_organization(None, client, io)?; + elevated_tmc_download(io, client, &config, &org)?; + } + }; + Ok(()) +} + +fn elevated_tmc_download( io: &mut Io, client: &mut Client, config: &TmcCliConfig, org: &str, ) -> anyhow::Result<()> { - use std::io::prelude::*; let temp_file_path = config.get_projects_dir(); let temp_file_path = temp_file_path.join("temp.txt"); let mut file = std::fs::File::open(temp_file_path.clone())?; @@ -191,7 +229,7 @@ pub fn elevated_download( let name_select = &vec[1]; // Get course by name - let course = match util::get_course_by_name(client, name_select, org)? { + let course = match util::get_course_by_name(client, name_select, &org)? { Some(course) => course, None => anyhow::bail!("Could not find course with that name"), }; diff --git a/src/commands/exercises.rs b/src/commands/exercises.rs deleted file mode 100644 index 3a3c610..0000000 --- a/src/commands/exercises.rs +++ /dev/null @@ -1,286 +0,0 @@ -use super::util::{self, choose_course}; -use crate::{ - client::Client, - io::{Io, PrintColor}, -}; -use tmc_langs::tmc::response::CourseExercise; - -/// Lists exercises for a given course -pub fn list_exercises( - io: &mut Io, - client: &mut Client, - course_name: Option<&str>, - org: &str, -) -> anyhow::Result<()> { - let fetched_course_name; - let name_select = if let Some(course_name) = course_name { - course_name - } else { - fetched_course_name = choose_course(io, client, org)?; - &fetched_course_name - }; - let course = util::get_course_by_name(client, name_select, org)? - .ok_or_else(|| anyhow::anyhow!("Could not find a course with name '{}'", name_select))?; - - let mut exercises = client.get_course_exercises(course.id)?; - exercises.sort_unstable_by(|l, r| l.name.cmp(&r.name)); - print_exercises(io, name_select, &exercises)?; - Ok(()) -} - -/// Prints information about given exercises -fn print_exercises( - io: &mut Io, - course_name: &str, - exercises: &[CourseExercise], -) -> anyhow::Result<()> { - io.println("", PrintColor::Normal)?; - io.println(&format!("Course name: {course_name}"), PrintColor::Normal)?; - - let none = "none"; - let mut prev_deadline = ""; - let mut prev_soft_deadline = ""; - for exercise in exercises { - // Skip locked and disabled exercises - if exercise.disabled || !exercise.unlocked { - continue; - } - - // Print deadline if it exists - if let Some(dl) = &exercise.deadline { - if prev_deadline != dl { - io.println(&format!("Deadline: {dl}"), PrintColor::Normal)?; - prev_deadline = dl; - } - } else if prev_deadline != none { - io.println(&format!("Deadline: {none}"), PrintColor::Normal)?; - prev_deadline = none; - } - - // TODO: Do we need soft deadline? - if let Some(dl) = &exercise.soft_deadline { - if prev_soft_deadline != dl { - io.println(&format!("Soft deadline: {dl}"), PrintColor::Normal)?; - prev_soft_deadline = dl; - } - } else if prev_soft_deadline != none { - io.println(&format!("Soft deadline: {none}"), PrintColor::Normal)?; - prev_soft_deadline = none; - } - - let mut completed = true; - let mut attempted = false; - - for point in &exercise.available_points { - if !exercise.awarded_points.contains(&point.name) { - completed = false; - } else { - attempted = true; - } - } - - let completion_status = if completed { - "Completed" - } else if attempted { - "Attempted" - } else { - "Not completed" - }; - - io.println( - &format!(" {}: {}", completion_status, &exercise.name), - PrintColor::Normal, - )?; - } - Ok(()) -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::test_helper::{self, TestSetup}; - use mockito::{Matcher, Mock, Server, ServerGuard}; - use tmc_langs::tmc::response::CourseExercise; - - fn mock_server() -> (ServerGuard, Vec) { - let mut server = Server::new(); - let mut mocks = Vec::new(); - mocks.push( - server - .mock("GET", "/api/v8/core/org/test/courses") - .match_query(Matcher::Any) - .with_body( - serde_json::json!([ - { - "id": 1, - "name": "course_name", - "title": "title", - "details_url": "example.com", - "unlock_url": "example.com", - "reviews_url": "example.com", - "comet_url": "example.com", - "spyware_urls": ["example.com"], - }, - ]) - .to_string(), - ) - .create(), - ); - mocks.push( - server - .mock("GET", "/api/v8/courses/1/exercises") - .match_query(Matcher::Any) - .with_body( - serde_json::json!([ - { - "id": 1, - "available_points": [], - "awarded_points": [], - "name": "part01-01_example_exercise", - "disabled": false, - "unlocked": true, - }, - { - "id": 2, - "available_points": [], - "awarded_points": [], - "name": "part02-03_example_valid", - "disabled": false, - "unlocked": true, - }, - ]) - .to_string(), - ) - .create(), - ); - (server, mocks) - } - - #[test] - fn list_exercises_test() { - test_helper::logging(); - let (mut input, mut output) = test_helper::input_output(); - let mut io = Io::new(&mut output, &mut input); - - let points = vec![ - //TODO: ExercisePoint is in private module - /*ExercisePoint { - id: 0, - exercise_id: 0, - name: "1.1".to_string(), - requires_review: true, - }*/ - ]; - let awarded_points = vec![/*"1.1".to_string()*/]; - - let exercises = [CourseExercise { - id: 0, - available_points: points, - awarded_points, - name: "part01-01_example_exercise".to_string(), - publish_time: None, - solution_visible_after: None, - deadline: None, - soft_deadline: None, - disabled: false, - unlocked: true, - }]; - - print_exercises(&mut io, "course_name", &exercises).unwrap(); - let output = String::from_utf8(output.into_inner()).unwrap(); - let output = output.lines().collect::>(); - assert!(output[0].eq(""), "first line should be empty"); - let course_string = "Course name: course_name"; - assert!( - output[1].eq(course_string), - "Expected '{}', got '{}'", - course_string, - output[1] - ); - let deadline_string = "Deadline: none"; - let soft_deadline_string = "Soft deadline: none"; - assert!( - output[2].eq(deadline_string), - "Expected '{}', got '{}'", - deadline_string, - output[2] - ); - assert!( - output[3].eq(soft_deadline_string), - "Expected '{}', got '{}'", - soft_deadline_string, - output[3] - ); - - let exercise_string = " Completed: part01-01_example_exercise"; - assert!( - output[4].eq(exercise_string), - "Expected '{}', got '{}'", - exercise_string, - output[4] - ); - } - - #[test] - fn list_exercises_with_client_test() { - test_helper::logging(); - let (server, _mocks) = mock_server(); - let (mut input, mut output) = test_helper::input_output(); - let TestSetup { - mut io, mut client, .. - } = test_helper::tmc_setup(&mut input, &mut output, &server); - client.set_tmc_token(test_helper::tmc_token()); - - list_exercises(&mut io, &mut client, Some("course_name"), "test").unwrap(); - - let output = String::from_utf8(output.into_inner()).unwrap(); - let output = output.lines().collect::>(); - assert!(output[0].eq(""), "first line should be empty"); - let course_string = "Course name: course_name"; - assert!( - output[1].eq(course_string), - "Expected '{}', got '{}'", - course_string, - output[1] - ); - - let deadline_string = "Deadline: none"; - let soft_deadline_string = "Soft deadline: none"; - assert!( - output[2].eq(deadline_string), - "Expected '{}', got '{}'", - deadline_string, - output[2] - ); - assert!( - output[3].eq(soft_deadline_string), - "Expected '{}', got '{}'", - soft_deadline_string, - output[3] - ); - - let exercise_string_1 = " Completed: part01-01_example_exercise"; - assert!( - output[4].eq(exercise_string_1), - "Expected '{}', got '{}'", - exercise_string_1, - output[4] - ); - - let exercise_string_2 = " Completed: part02-03_example_valid"; - assert!( - output[5].eq(exercise_string_2), - "Expected '{}', got '{}'", - exercise_string_2, - output[5] - ); - - let expected_size = 6; - assert!( - output.len().eq(&expected_size), - "Expected size '{}', got {}", - expected_size, - output.len() - ); - } -} diff --git a/src/commands/login.rs b/src/commands/login.rs index 502dc72..fcec709 100644 --- a/src/commands/login.rs +++ b/src/commands/login.rs @@ -1,3 +1,4 @@ +use super::util; use crate::{ client::Client, config::TmcCliConfig, @@ -5,6 +6,7 @@ use crate::{ }; pub fn login(io: &mut Io, client: &mut Client, config: &mut TmcCliConfig) -> anyhow::Result<()> { + util::require_logged_out(client, &config)?; io.print("Email / username: ", PrintColor::Normal)?; let mut username = io.read_line()?; username = username.trim().to_string(); diff --git a/src/commands/logout.rs b/src/commands/logout.rs index 500f4c5..3f14788 100644 --- a/src/commands/logout.rs +++ b/src/commands/logout.rs @@ -1,3 +1,4 @@ +use super::util; use crate::{ client::Client, config::TmcCliConfig, @@ -5,6 +6,7 @@ use crate::{ }; pub fn logout(io: &mut Io, client: &mut Client, config: &mut TmcCliConfig) -> anyhow::Result<()> { + util::require_logged_in(client, &config)?; client.logout(config)?; io.println("Logged out successfully.", PrintColor::Success)?; Ok(()) diff --git a/src/commands/mooc.rs b/src/commands/mooc.rs index 4e37306..3b9b3ae 100644 --- a/src/commands/mooc.rs +++ b/src/commands/mooc.rs @@ -1,7 +1,5 @@ //! Contains courses.mooc.fi-related commands. -pub mod course_exercises; -pub mod courses; pub mod download_exercises; pub mod submit_exercise; pub mod update_exercises; @@ -9,7 +7,6 @@ pub mod update_exercises; use crate::{ client::Client, config::{LocalMoocExercise, TmcCliConfig}, - Io, PrintColor, }; use anyhow::Context; use std::{ @@ -27,14 +24,12 @@ use tmc_langs::{ // Ok(None) => user declined to select a course // Err => something went wrong fn get_course_by_slug_or_selection( - io: &mut Io, client: &mut Client, slug: Option<&str>, -) -> anyhow::Result> { - let courses = client.mooc_courses()?; +) -> anyhow::Result { + let courses = client.enrolled_mooc_courses()?; if courses.is_empty() { - io.println("No enrolled courses found", PrintColor::Normal)?; - return Ok(None); + anyhow::bail!("No enrolled courses found"); } let course = if let Some(slug) = slug { @@ -55,29 +50,28 @@ fn get_course_by_slug_or_selection( .map(String::as_str) .collect::>(); - match crate::interactive::interactive_list("Select course:", &keys)? { + let help_message = "If the course you're looking for is not listed here, please log in to https://courses.mooc.fi using the same account you use here. Then, access the course material and choose a course instance from the dialog that pops up."; + match crate::interactive::interactive_list("Select course:", &keys, Some(help_message))? { Some(selection) => course_name_to_course .remove(&selection) .expect("Invalid selection"), None => { - io.print("Did not select a course", PrintColor::Normal)?; - return Ok(None); + anyhow::bail!("Did not select a course"); } } }; - Ok(Some(course)) + Ok(course) } // Ok(Some(exercise)) => selected an exercise // Ok(None) => user declined to select an exercise // Err => something went wrong fn select_exercise( - io: &mut Io, course: &CourseInstance, config: &TmcCliConfig, -) -> anyhow::Result> { - let exercises = config.get_mooc_exercises(); +) -> anyhow::Result { + let exercises = config.get_local_mooc_exercises(); let course_exercises = exercises .deref() .iter() @@ -93,17 +87,16 @@ fn select_exercise( .map(|s| s.as_str()) .collect::>(); - let course = match crate::interactive::interactive_list("Select exercise:", &keys)? { + let course = match crate::interactive::interactive_list("Select exercise:", &keys, None)? { Some(selection) => exercise_name_to_exercise .remove(&selection) .expect("Invalid selection"), None => { - io.print("Did not select an exercise", PrintColor::Normal)?; - return Ok(None); + anyhow::bail!("Did not select an exercise"); } }; - Ok(Some(course.clone())) + Ok(course.clone()) } fn course_identifier(course: &CourseInstance) -> String { @@ -117,12 +110,19 @@ fn course_identifier(course: &CourseInstance) -> String { ) } -fn exercise_path(root: &Path, course_slug: &str, exercise: &TmcExerciseSlide) -> PathBuf { +fn exercise_path( + root: &Path, + course_instance: &CourseInstance, + exercise: &TmcExerciseSlide, +) -> PathBuf { let dir_name = format!( "{}-{}", exercise.exercise_order_number, exercise.exercise_name ); - root.join("courses").join(course_slug).join(dir_name) + root.join("courses") + .join(&course_instance.course_slug) + .join(course_instance.id.to_string()) + .join(dir_name) } fn download_and_extract_exercise( diff --git a/src/commands/mooc/course_exercises.rs b/src/commands/mooc/course_exercises.rs deleted file mode 100644 index 152a599..0000000 --- a/src/commands/mooc/course_exercises.rs +++ /dev/null @@ -1,33 +0,0 @@ -use crate::{client::Client, Io, PrintColor}; -use tmc_langs::mooc::{CourseInstance, TmcExerciseSlide}; - -pub fn run(io: &mut Io, client: &mut Client, slug: Option<&str>) -> anyhow::Result<()> { - let Some(course) = super::get_course_by_slug_or_selection(io, client, slug)? else { - return Ok(()); - }; - - let mut exercises = client.mooc_course_exercises(course.id)?; - exercises.sort_by_key(|e| e.exercise_order_number); - print_exercises(io, &course, &exercises)?; - - Ok(()) -} - -/// Prints information about given exercises -fn print_exercises( - io: &mut Io, - course: &CourseInstance, - exercises: &[TmcExerciseSlide], -) -> anyhow::Result<()> { - let course_identifier = super::course_identifier(course); - io.println( - &format!("\nActive exercises on {}", course_identifier), - PrintColor::Normal, - )?; - - for exercise in exercises { - io.println(&format!(" {}", exercise.exercise_name), PrintColor::Normal)?; - } - - Ok(()) -} diff --git a/src/commands/mooc/courses.rs b/src/commands/mooc/courses.rs deleted file mode 100644 index 5d90cbf..0000000 --- a/src/commands/mooc/courses.rs +++ /dev/null @@ -1,28 +0,0 @@ -use crate::{client::Client, Io, PrintColor}; -use tmc_langs::mooc::CourseInstance; - -pub fn run(io: &mut Io, client: &mut Client) -> anyhow::Result<()> { - let mut courses = client.mooc_courses()?; - courses.sort_by_cached_key(|c| c.course_name.clone()); - print_courses(io, &courses)?; - Ok(()) -} - -/// Prints information about given exercises -fn print_courses(io: &mut Io, courses: &[CourseInstance]) -> anyhow::Result<()> { - if courses.is_empty() { - io.println("No enrolled courses found", PrintColor::Normal)?; - return Ok(()); - } - - io.println( - "\nCurrently enrolled courses.mooc.fi courses", - PrintColor::Normal, - )?; - for course in courses { - let course_identifier = super::course_identifier(course); - io.println(&format!(" {}", course_identifier), PrintColor::Normal)?; - } - - Ok(()) -} diff --git a/src/commands/mooc/download_exercises.rs b/src/commands/mooc/download_exercises.rs index 88d15b6..acd4611 100644 --- a/src/commands/mooc/download_exercises.rs +++ b/src/commands/mooc/download_exercises.rs @@ -14,16 +14,21 @@ pub fn run( current_dir: bool, config: &mut TmcCliConfig, ) -> anyhow::Result<()> { - let Some(course) = super::get_course_by_slug_or_selection(io, client, slug)? else { - return Ok(()); - }; - + let course = super::get_course_by_slug_or_selection(client, slug)?; let exercises = client.mooc_course_exercises(course.id)?; download_exercises(io, client, &course, exercises, current_dir, config)?; Ok(()) } +#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] +struct LocalMoocExerciseKey { + course_instance_id: Uuid, + exercise_id: Uuid, + slide_id: Uuid, + task_id: Uuid, +} + /// Prints information about given exercises fn download_exercises( io: &mut Io, @@ -39,12 +44,28 @@ fn download_exercises( config.get_projects_dir().to_path_buf() }; let local_exercise_map = config - .get_mooc_exercises() + .get_local_mooc_exercises() .iter() .cloned() - .map(|e| ((e.exercise_id, e.slide_id, e.task_id), e)) + .map(|e| { + ( + LocalMoocExerciseKey { + course_instance_id: e.course_instance_id, + exercise_id: e.exercise_id, + slide_id: e.slide_id, + task_id: e.task_id, + }, + e, + ) + }) .collect::>(); + if exercises.is_empty() { + io.println( + &format!("No TestMyCode exercises for '{}' found", course.course_name,), + PrintColor::Normal, + )?; + } io.println( &format!( "Downloading exercises for '{}' into '{}'...", @@ -83,19 +104,24 @@ fn download_exercise( io: &mut Io, client: &mut Client, config: &mut TmcCliConfig, - course: &CourseInstance, + course_instance: &CourseInstance, projects_dir: &Path, exercise: TmcExerciseSlide, - local_exercise_map: &HashMap<(Uuid, Uuid, Uuid), LocalMoocExercise>, + local_exercise_map: &HashMap, ) -> anyhow::Result<()> { if exercise.tasks.len() > 1 { io.println("Exercise contains more than one task, but only one task per exercise is currently supported by the CLI. Only the first task will be processed.", PrintColor::Failed)?; } - let download_location = super::exercise_path(projects_dir, &course.course_slug, &exercise); + let download_location = super::exercise_path(projects_dir, course_instance, &exercise); // only take first task if let Some(task) = exercise.tasks.into_iter().next() { - let existing_exercise = - local_exercise_map.get(&(exercise.exercise_id, exercise.slide_id, task.task_id)); + let key = LocalMoocExerciseKey { + course_instance_id: course_instance.id, + exercise_id: exercise.exercise_id, + slide_id: exercise.slide_id, + task_id: task.task_id, + }; + let existing_exercise = local_exercise_map.get(&key); match task.public_spec { Some(PublicSpec::Editor { @@ -135,7 +161,7 @@ fn download_exercise( )?; } config.add_mooc_exercise(LocalMoocExercise { - course_instance_id: course.id, + course_instance_id: course_instance.id, exercise_name: exercise.exercise_name, exercise_id: exercise.exercise_id, slide_id: exercise.slide_id, diff --git a/src/commands/mooc/submit_exercise.rs b/src/commands/mooc/submit_exercise.rs index 4404308..11010fb 100644 --- a/src/commands/mooc/submit_exercise.rs +++ b/src/commands/mooc/submit_exercise.rs @@ -15,10 +15,7 @@ pub fn run( path: Option<&Path>, config: &TmcCliConfig, ) -> anyhow::Result<()> { - let Some(exercise) = select_exercise(io, client, path, config)? else { - return Ok(()); - }; - + let exercise = select_exercise(client, path, config)?; io.println("Packaging the submission...", PrintColor::Normal)?; let temp_file = tmc_langs::file_util::named_temp_file()?; tmc_langs::compress_project_to( @@ -98,17 +95,15 @@ pub fn run( } fn select_exercise( - io: &mut Io, client: &mut Client, path: Option<&Path>, - config: &TmcCliConfig, -) -> anyhow::Result> { +) -> anyhow::Result { match path { Some(path) => { // try to find exercise details for path let canon_path = path.canonicalize()?; - let exercises = config.get_mooc_exercises(); + let exercises = config.get_local_mooc_exercises(); let exercise = exercises .deref() .iter() @@ -116,16 +111,12 @@ fn select_exercise( .ok_or_else(|| { anyhow::anyhow!("Failed to find exercise for the path '{}'", path.display()) })?; - Ok(Some(exercise.clone())) + Ok(exercise.clone()) } None => { - let Some(course) = super::get_course_by_slug_or_selection(io, client, None)? else { - return Ok(None); - }; - let Some(exercise) = super::select_exercise(io, &course, config)? else { - return Ok(None); - }; - Ok(Some(exercise)) + let course = super::get_course_by_slug_or_selection(client, None)?; + let exercise = super::select_exercise(&course, config)?; + Ok(exercise) } } } diff --git a/src/commands/mooc/update_exercises.rs b/src/commands/mooc/update_exercises.rs index 0fd3bf4..9551f1e 100644 --- a/src/commands/mooc/update_exercises.rs +++ b/src/commands/mooc/update_exercises.rs @@ -7,26 +7,20 @@ use std::{collections::HashMap, ops::DerefMut}; use tmc_langs::mooc::{PublicSpec, TmcExerciseTask}; use uuid::Uuid; -pub fn run( - io: &mut Io, - client: &mut Client, - course: Option<&str>, - config: &mut TmcCliConfig, -) -> anyhow::Result<()> { - let Some(course) = super::get_course_by_slug_or_selection(io, client, course)? else { - return Ok(()); - }; - - let mooc_exercises = client - .mooc_course_exercises(course.id)? - .into_iter() - .flat_map(|e| { - e.tasks - .into_iter() - .map(move |t| ((e.exercise_id, e.slide_id, t.task_id), t)) - }) - .collect::>(); - update_exercises(io, client, mooc_exercises, config)?; +pub fn run(io: &mut Io, client: &mut Client, config: &mut TmcCliConfig) -> anyhow::Result<()> { + let courses = client.enrolled_mooc_courses()?; + for course in courses { + let mooc_exercises = client + .mooc_course_exercises(course.id)? + .into_iter() + .flat_map(|e| { + e.tasks + .into_iter() + .map(move |t| ((e.exercise_id, e.slide_id, t.task_id), t)) + }) + .collect::>(); + update_exercises(io, client, mooc_exercises, config)?; + } Ok(()) } @@ -37,7 +31,7 @@ fn update_exercises( mooc_exercises: HashMap<(Uuid, Uuid, Uuid), TmcExerciseTask>, config: &mut TmcCliConfig, ) -> anyhow::Result<()> { - for local_exercise in config.get_mooc_exercises().deref_mut() { + for local_exercise in config.get_local_mooc_exercises().deref_mut() { if let Err(err) = update_exercise(io, client, local_exercise, &mooc_exercises) { // ignore (highly unlikely) logging errors, we'll want to save the config either way let _ = io.println( diff --git a/src/commands/organization.rs b/src/commands/organization.rs deleted file mode 100644 index 3852eee..0000000 --- a/src/commands/organization.rs +++ /dev/null @@ -1,102 +0,0 @@ -use crate::{ - client::Client, - config::TmcCliConfig, - interactive::{self, interactive_list}, - io::{Io, PrintColor}, -}; - -// Asks for organization from user and saves it into file -pub fn set_organization_old( - io: &mut Io, - client: &mut Client, - config: &mut TmcCliConfig, -) -> anyhow::Result { - // List all organizations - let mut orgs = client.get_organizations()?; - orgs.sort_by(|a, b| b.pinned.cmp(&a.pinned).then(b.name.cmp(&a.name))); - let mut last_pinned = true; - - io.println("Available Organizations:", PrintColor::Normal)?; - io.println("", PrintColor::Normal)?; - - for org in &orgs { - if org.pinned != last_pinned { - io.println("----------", PrintColor::Normal)?; - } - io.print(&org.name, PrintColor::Normal)?; - io.print(" Slug: ", PrintColor::Normal)?; - io.println(&org.slug, PrintColor::Normal)?; - last_pinned = org.pinned; - } - - io.print( - "\nChoose organization by writing its slug: ", - PrintColor::Normal, - )?; - let slug = io.read_line()?.trim().to_string(); - - if let Some(org) = orgs.into_iter().find(|org| org.slug == slug) { - config.set_organization(org.slug); - config.save()?; - return Ok(org.name); - } - - anyhow::bail!("No such organization for the given slug: {}", slug); -} - -pub fn set_organization( - io: &mut Io, - client: &mut Client, - config: &mut TmcCliConfig, -) -> anyhow::Result { - io.println("Fetching organizations...", PrintColor::Normal)?; - let mut orgs = client.get_organizations()?; - orgs.sort_by(|a, b| b.pinned.cmp(&a.pinned).then(a.name.cmp(&b.name))); - let mut pinned = orgs - .iter() - .filter(|org| org.pinned) - .map(|org| org.name.as_str()) - .collect::>(); - - let others = String::from("View all organizations"); - pinned.push(others.as_str()); - - let prompt = String::from("Select your organization: "); - let selection = interactive::interactive_list(&prompt, &pinned)? - .ok_or_else(|| anyhow::anyhow!("Didn't select any organization"))?; - let org_name = if selection == others { - let all = orgs.iter().map(|org| org.name.as_str()).collect::>(); - interactive_list(&prompt, &all)? - .ok_or_else(|| anyhow::anyhow!("Didn't select any organization"))? - } else { - selection - }; - - if let Some(org) = orgs.into_iter().find(|org| org.name == org_name) { - config.set_organization(org.slug); - config.save()?; - return Ok(org.name); - } - - anyhow::bail!("Something strange happened"); -} - -// Check if logged in, then ask for organization -pub fn organization( - io: &mut Io, - client: &mut Client, - interactive_mode: bool, - config: &mut TmcCliConfig, -) -> anyhow::Result<()> { - let org = if interactive_mode { - set_organization(io, client, config)? - } else { - set_organization_old(io, client, config)? - }; - - io.println( - &format!("Selected {org} as organization."), - PrintColor::Success, - )?; - Ok(()) -} diff --git a/src/commands/paste.rs b/src/commands/paste.rs index edc5553..921e474 100644 --- a/src/commands/paste.rs +++ b/src/commands/paste.rs @@ -1,4 +1,4 @@ -use super::util; +use super::{util, Platform}; use crate::{ client::Client, config::TmcCliConfig, @@ -9,6 +9,24 @@ use crate::{ use anyhow::Context; use tmc_langs::{tmc::ClientUpdateData, Language}; +pub fn paste( + io: &mut Io, + client: &mut Client, + config: &mut TmcCliConfig, + path: Option<&str>, +) -> anyhow::Result<()> { + util::ensure_logged_in(client, io, config)?; + match util::select_courses_or_tmc()? { + Platform::Mooc => { + todo!() + } + Platform::Tmc => { + tmc_paste(io, client, path, &config)?; + } + }; + Ok(()) +} + /// Sends the course exercise submission with paste message to the server. /// Path to the exercise can be given as a parameter or /// the user can run the command in the exercise folder. @@ -16,7 +34,7 @@ use tmc_langs::{tmc::ClientUpdateData, Language}; /// # Errors /// Returns an error if no exercise found on given path or current folder. /// Returns an error if user is not logged in. -pub fn paste( +pub fn tmc_paste( io: &mut Io, client: &mut Client, path: Option<&str>, diff --git a/src/commands/submit.rs b/src/commands/submit.rs index 6032ad3..9ccb6a4 100644 --- a/src/commands/submit.rs +++ b/src/commands/submit.rs @@ -1,4 +1,4 @@ -use super::util; +use super::{mooc, util, Platform}; use crate::{ client::Client, config::TmcCliConfig, @@ -13,6 +13,24 @@ use tmc_langs::{ Language, }; +pub fn submit( + io: &mut Io, + client: &mut Client, + config: &mut TmcCliConfig, + path: Option<&str>, +) -> anyhow::Result<()> { + util::ensure_logged_in(client, io, config)?; + match util::select_courses_or_tmc()? { + Platform::Mooc => { + mooc::submit_exercise::run(io, client, None, &config)?; + } + Platform::Tmc => { + tmc_submit(io, client, path, &config)?; + } + }; + Ok(()) +} + /// Sends the course exercise submission to the server. /// Path to the exercise can be given as a parameter or /// the user can run the command in the exercise folder. @@ -20,7 +38,7 @@ use tmc_langs::{ /// # Errors /// Returns an error if no exercise was found on given path or current folder. /// Returns an error if user is not logged in. -pub fn submit( +pub fn tmc_submit( io: &mut Io, client: &mut Client, path: Option<&str>, diff --git a/src/commands/test.rs b/src/commands/test.rs index 34721fc..a87c5d4 100644 --- a/src/commands/test.rs +++ b/src/commands/test.rs @@ -9,7 +9,7 @@ use tmc_langs::RunResult; /// Executes tmc tests for one exercise. If path not given, check if current folder is an exercise. /// If not, asks exercise with an interactive menu. -pub fn test(io: &mut Io, path: Option<&str>, config: &TmcCliConfig) -> anyhow::Result<()> { +pub fn test(io: &mut Io, config: &TmcCliConfig, path: Option<&str>) -> anyhow::Result<()> { // todo: use context let exercise_path = util::exercise_pathfinder(path, config).context("Error finding exercise")?; diff --git a/src/commands/update.rs b/src/commands/update.rs index b28dbb0..87ddf60 100644 --- a/src/commands/update.rs +++ b/src/commands/update.rs @@ -1,13 +1,29 @@ +use super::{mooc, util, Platform}; use crate::{ client::Client, config::TmcCliConfig, io::{Io, PrintColor}, }; use anyhow::Context; -use std::{ - path::{Path, PathBuf}, - process::Command, -}; +use std::{path::PathBuf, process::Command}; + +pub fn update( + io: &mut Io, + client: &mut Client, + config: &mut TmcCliConfig, + current_dir: bool, +) -> anyhow::Result<()> { + util::ensure_logged_in(client, io, config)?; + match util::select_courses_or_tmc()? { + Platform::Mooc => { + mooc::update_exercises::run(io, client, config)?; + } + Platform::Tmc => { + tmc_update(io, client, current_dir, config)?; + } + }; + Ok(()) +} /// Updates exercises from project dir or current directory. /// Update is ran only if local exercise checksums differ from @@ -22,11 +38,11 @@ use std::{ /// tmc update //runs update command in project dir /// tmc update -d //runs update command in current dir /// -pub fn update( +fn tmc_update( io: &mut Io, client: &mut Client, current_dir: bool, - config: &TmcCliConfig, + config: &mut TmcCliConfig, ) -> anyhow::Result<()> { // Get a client that has credentials client.load_login(config)?; @@ -36,12 +52,21 @@ pub fn update( config.get_projects_dir().to_path_buf() }; let tmp_path = path.to_str().context("invalid path")?; - match call_update(&path, client) { - Ok(msg) => io.println(&format!("\n{msg}"), PrintColor::Success)?, - Err(msg) => { + match client.update_exercises(&path) { + Ok(_) => { + io.println( + &format!( + "Exercises updated succesfully to {}", + path.to_str().context("invalid path")? + ), + PrintColor::Success, + )?; + } + Err(err) => { let os = std::env::consts::OS; + let err = anyhow::format_err!(err); if os == "windows" - && msg + && err .chain() .any(|e| e.to_string().contains("Failed to create file")) { @@ -66,22 +91,31 @@ pub fn update( .spawn() .context("launch failure")?; } else { - anyhow::bail!(msg); + anyhow::bail!(err); } } } Ok(()) } -fn call_update(path: &Path, client: &mut Client) -> anyhow::Result { - client.update_exercises(path)?; - Ok(format!( - "Exercises updated succesfully to {}", - path.to_str().context("invalid path")? - )) +pub fn elevated_update( + io: &mut Io, + client: &mut Client, + config: &mut TmcCliConfig, +) -> anyhow::Result<()> { + util::ensure_logged_in(client, io, config)?; + match util::select_courses_or_tmc()? { + Platform::Mooc => { + todo!() + } + Platform::Tmc => { + elevated_tmc_update(io, client, config)?; + } + }; + Ok(()) } -pub fn elevated_update( +fn elevated_tmc_update( io: &mut Io, client: &mut Client, config: &TmcCliConfig, @@ -95,8 +129,14 @@ pub fn elevated_update( std::fs::remove_file(temp_file_path)?; let path = PathBuf::from(params); io.println("", PrintColor::Normal)?; - let msg = call_update(&path, client)?; - io.println(&msg, PrintColor::Success)?; + client.update_exercises(&path)?; + io.println( + &format!( + "Exercises updated succesfully to {}", + path.to_str().context("invalid path")? + ), + PrintColor::Success, + )?; pause()?; Ok(()) } diff --git a/src/commands/util.rs b/src/commands/util.rs index ce54996..ebca086 100644 --- a/src/commands/util.rs +++ b/src/commands/util.rs @@ -1,3 +1,6 @@ +pub mod organization; + +use super::Platform; use crate::{ client::Client, config::TmcCliConfig, @@ -9,6 +12,63 @@ use anyhow::Context; use std::{env, path::PathBuf}; use tmc_langs::{tmc::response::Course, Credentials, ProjectsConfig}; +pub fn require_logged_out(client: &mut Client, config: &TmcCliConfig) -> anyhow::Result<()> { + let exists = client.load_login(config).is_ok(); + if exists { + anyhow::bail!("Already logged in. Please logout first with 'tmc logout'"); + } + Ok(()) +} + +pub fn ensure_logged_in( + client: &mut Client, + io: &mut Io, + config: &mut TmcCliConfig, +) -> anyhow::Result<()> { + let exists = client.load_login(config).is_ok(); + if !exists { + super::login::login(io, client, config)?; + } + Ok(()) +} + +pub fn require_logged_in(client: &mut Client, config: &TmcCliConfig) -> anyhow::Result<()> { + let exists = client.load_login(config).is_ok(); + if !exists { + anyhow::bail!("Not logged in."); + } + Ok(()) +} + +pub fn get_or_select_organization( + org_arg: Option, + client: &mut Client, + io: &mut Io, +) -> anyhow::Result { + // priority to cli arg + if let Some(org_param) = org_arg { + return Ok(org_param); + } + // otherwise we ask the user + let org = self::organization::select_organization(io, client)?; + Ok(org) +} + +pub fn select_courses_or_tmc() -> anyhow::Result { + const MOOC: &str = "https://courses.mooc.fi/"; + const TMC: &str = "https://tmc.mooc.fi/"; + let selection = interactive::interactive_list("Select MOOC platform", &[MOOC, TMC], None)?; + let platform = match selection.as_deref() { + Some(MOOC) => Platform::Mooc, + Some(TMC) => Platform::Tmc, + Some(_) => unreachable!(), + None => { + anyhow::bail!("Did not select a platform"); + } + }; + Ok(platform) +} + pub fn get_credentials() -> Option { // Load login credentials if they exist in the file Credentials::load(PLUGIN).unwrap_or(None) @@ -64,38 +124,25 @@ pub fn exercise_pathfinder(path: Option<&str>, config: &TmcCliConfig) -> anyhow: pub fn choose_course(io: &mut Io, client: &mut Client, org: &str) -> anyhow::Result { io.println("Fetching courses...", PrintColor::Normal)?; - let courses = client - .list_courses(org) - .context("Could not list courses.")?; - - let mut courses = courses - .iter() - .map(|course| client.get_course_details(course.id)) - .collect::, _>>()?; - courses.sort_by(|a, b| { - a.course - .title - .to_lowercase() - .cmp(&b.course.title.to_lowercase()) - }); + let mut courses = client.list_courses(org).context("Could not list courses")?; + courses.sort_by(|a, b| a.title.to_lowercase().cmp(&b.title.to_lowercase())); let course = get_course_name( &courses .iter() - .map(|course| course.course.title.as_str()) + .map(|course| course.title.as_str()) .collect::>(), )?; let selection = courses .into_iter() - .find(|c| c.course.title == course) + .find(|c| c.title == course) .context("No course with the selected name was found")? - .course .name; Ok(selection) } pub fn get_course_name(courses: &[&str]) -> anyhow::Result { - let course = interactive::interactive_list("Select your course:", courses)? - .ok_or_else(|| anyhow::anyhow!("Didn't select any course"))?; + let course = interactive::interactive_list("Select your course:", courses, None)? + .ok_or_else(|| anyhow::anyhow!("Did not select any course"))?; if course.is_empty() { anyhow::bail!("Could not find a course by the given title"); @@ -128,8 +175,8 @@ pub fn choose_exercise(config: &TmcCliConfig) -> anyhow::Result { ); } - let chosen_course = interactive_list("First select course: ", &courses)? - .ok_or_else(|| anyhow::anyhow!("Didn't select any course"))?; + let chosen_course = interactive_list("First select course: ", &courses, None)? + .ok_or_else(|| anyhow::anyhow!("Did not select any course"))?; let course_config = projects_config .courses @@ -152,8 +199,8 @@ pub fn choose_exercise(config: &TmcCliConfig) -> anyhow::Result { ); } - let chosen_exercise = interactive_list("Select exercise: ", &exercise_list)? - .ok_or_else(|| anyhow::anyhow!("Didn't select any exercise"))?; + let chosen_exercise = interactive_list("Select exercise: ", &exercise_list, None)? + .ok_or_else(|| anyhow::anyhow!("Did not select any exercise"))?; let mut path = config.get_projects_dir().to_path_buf(); path.push(chosen_course); diff --git a/src/commands/util/organization.rs b/src/commands/util/organization.rs new file mode 100644 index 0000000..420cdbe --- /dev/null +++ b/src/commands/util/organization.rs @@ -0,0 +1,47 @@ +use crate::{ + client::Client, + interactive, + io::{Io, PrintColor}, +}; +use tmc_langs::tmc::response::Organization; + +pub fn select_organization(io: &mut Io, client: &mut Client) -> anyhow::Result { + let org = select_organization_inner(io, client)?; + + io.println( + &format!("Selected {} as organization.", org.name), + PrintColor::Success, + )?; + Ok(org.slug) +} + +fn select_organization_inner(io: &mut Io, client: &mut Client) -> anyhow::Result { + io.println("Fetching organizations...", PrintColor::Normal)?; + let mut orgs = client.get_organizations()?; + orgs.sort_by(|a, b| b.pinned.cmp(&a.pinned).then(a.name.cmp(&b.name))); + let mut pinned = orgs + .iter() + .filter(|org| org.pinned) + .map(|org| org.name.as_str()) + .collect::>(); + + let others = String::from("View all organizations"); + pinned.push(others.as_str()); + + let prompt = String::from("Select your organization: "); + let selection = interactive::interactive_list(&prompt, &pinned, None)? + .ok_or_else(|| anyhow::anyhow!("Did not select any organization"))?; + let org_name = if selection == others { + let all = orgs.iter().map(|org| org.name.as_str()).collect::>(); + interactive::interactive_list(&prompt, &all, None)? + .ok_or_else(|| anyhow::anyhow!("Did not select any organization"))? + } else { + selection + }; + + if let Some(org) = orgs.into_iter().find(|org| org.name == org_name) { + return Ok(org); + } + + anyhow::bail!("Something strange happened"); +} diff --git a/src/config.rs b/src/config.rs index 91fdfc6..5b89ef5 100644 --- a/src/config.rs +++ b/src/config.rs @@ -10,7 +10,6 @@ use std::{ use tmc_langs::TmcConfig; use uuid::Uuid; -const ORGANIZATION_KEY: &str = "organization"; const TEST_LOGIN_KEY: &str = "test_login"; const TEST_LOGIN_VALUE: &str = "test_logged_in"; const MOOC_EXERCISES_KEY: &str = "mooc_exercises"; @@ -59,15 +58,6 @@ impl TmcCliConfig { self.config.get_projects_dir() } - pub fn get_organization(&self) -> Option<&str> { - self.config.get(ORGANIZATION_KEY).and_then(|v| v.as_str()) - } - - pub fn set_organization(&mut self, org: String) { - self.config - .insert(ORGANIZATION_KEY.to_string(), toml::Value::String(org)); - } - pub fn get_test_login(&self) -> Option<&str> { self.config.get(TEST_LOGIN_KEY).and_then(|v| v.as_str()) } @@ -82,22 +72,7 @@ impl TmcCliConfig { self.config.remove(TEST_LOGIN_KEY); } - pub fn get_mooc_exercises(&self) -> RefMut<'_, Vec> { - self.init_mooc_exercises() - } - - // ensures no duplicates get added - pub fn add_mooc_exercise(&mut self, exercise: LocalMoocExercise) { - let mut exercises = self.init_mooc_exercises(); - let existing = exercises.deref_mut().iter_mut().find(|e| e == &&exercise); - if let Some(existing) = existing { - *existing = exercise; - } else { - exercises.push(exercise); - } - } - - fn init_mooc_exercises(&self) -> RefMut<'_, Vec> { + pub fn get_local_mooc_exercises(&self) -> RefMut<'_, Vec> { RefMut::map(self.mooc_exercises.borrow_mut(), |ex| match ex { Some(exercises) => exercises, None => { @@ -110,6 +85,17 @@ impl TmcCliConfig { } }) } + + // ensures no duplicates get added + pub fn add_mooc_exercise(&mut self, exercise: LocalMoocExercise) { + let mut exercises = self.get_local_mooc_exercises(); + let existing = exercises.deref_mut().iter_mut().find(|e| e == &&exercise); + if let Some(existing) = existing { + *existing = exercise; + } else { + exercises.push(exercise); + } + } } #[derive(Debug, Clone, Serialize, Deserialize)] diff --git a/src/interactive.rs b/src/interactive.rs index c1085f3..c97f280 100644 --- a/src/interactive.rs +++ b/src/interactive.rs @@ -1,4 +1,4 @@ pub mod prompt; pub mod state; -pub use self::{prompt::*, state::*}; +pub use self::prompt::*; diff --git a/src/interactive/prompt.rs b/src/interactive/prompt.rs index 933f869..1f2cb73 100644 --- a/src/interactive/prompt.rs +++ b/src/interactive/prompt.rs @@ -1,18 +1,18 @@ -use super::state::AppState; +use super::state::ListView; use crossterm::{ event::{poll, read, Event, KeyCode, KeyModifiers}, execute, terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen}, }; -use std::{io::stdout, time::Duration}; -use tui::{ +use ratatui::{ backend::{Backend, CrosstermBackend}, layout::{Constraint, Direction, Layout}, style::{Modifier, Style}, - text::{Span, Spans}, + text::Span, widgets::{Block, Borders, List, ListItem, Paragraph, Wrap}, Terminal, }; +use std::{io::stdout, time::Duration}; /// control the maximum waiting time for event availability /// in this case, the value should not really matter, @@ -35,14 +35,18 @@ const POLL_RATE: u64 = 1000; /// println!("You chose: {}", choice); /// } /// ``` -pub fn interactive_list(prompt: &str, items: &[&str]) -> anyhow::Result> { +pub fn interactive_list( + prompt: &str, + items: &[&str], + help: Option<&str>, +) -> anyhow::Result> { execute!(stdout(), EnterAlternateScreen)?; let backend = CrosstermBackend::new(stdout()); let mut terminal = Terminal::new(backend)?; terminal.clear()?; enable_raw_mode()?; - let mut app = AppState::new(items); + let mut app = ListView::new(items, help); let result = event_loop(&mut terminal, &mut app, prompt)?; disable_raw_mode()?; @@ -55,31 +59,40 @@ pub fn interactive_list(prompt: &str, items: &[&str]) -> anyhow::Result( terminal: &mut Terminal, - app: &mut AppState, + app: &mut ListView, prompt: &str, ) -> anyhow::Result<()> where B: Backend, { terminal.draw(|f| { - let chunks = Layout::default() + let vertical_layout = if app.help.is_some() { + Layout::default() + .direction(Direction::Vertical) + .constraints([Constraint::Percentage(80), Constraint::Percentage(20)].as_slice()) + .split(f.size()) + } else { + Layout::default() + .direction(Direction::Vertical) + .constraints([Constraint::Percentage(100)].as_slice()) + .split(f.size()) + }; + let top_layout = Layout::default() .direction(Direction::Horizontal) - .constraints([Constraint::Percentage(80), Constraint::Percentage(20)].as_ref()) - .split(f.size()); + .constraints([Constraint::Percentage(80), Constraint::Percentage(20)].as_slice()) + .split(vertical_layout[0]); + let items: Vec = app .items .displayed .iter() - .map(|i| { - let lines = vec![Spans::from(*i)]; - ListItem::new(lines).style(Style::default()) - }) + .map(|i| ListItem::new(*i).style(Style::default())) .collect(); let items = List::new(items) .block(Block::default().borders(Borders::NONE).title(prompt)) .highlight_style(Style::default().add_modifier(Modifier::BOLD)) .highlight_symbol(">> "); - f.render_stateful_widget(items, chunks[0], &mut app.items.state); + f.render_stateful_widget(items, top_layout[0], &mut app.items.state); // if the user hasn't written anything yet, display the help message in its place let text = if app.filter.is_empty() { @@ -91,7 +104,11 @@ where } else { Paragraph::new(Span::raw(app.filter.clone())).wrap(Wrap { trim: true }) }; - f.render_widget(text, chunks[1]); + f.render_widget(text, top_layout[1]); + if let Some(help) = app.help { + let text = Paragraph::new(Span::raw(help)).wrap(Wrap { trim: false }); + f.render_widget(text, vertical_layout[1]); + } })?; Ok(()) } @@ -102,7 +119,7 @@ where /// None: nothing was selected yet /// Some(None): the user selected nothing (quit with ESC) /// Some(Some(res)): the user selected an item -fn read_keys(app: &mut AppState) -> anyhow::Result>> { +fn read_keys(app: &mut ListView) -> anyhow::Result>> { if poll(Duration::from_millis(POLL_RATE))? { if let Event::Key(x) = read()? { // CTRL-C is the usual stop command @@ -142,7 +159,7 @@ fn read_keys(app: &mut AppState) -> anyhow::Result>> { fn event_loop( terminal: &mut Terminal, - app: &mut AppState<'_>, + app: &mut ListView<'_>, prompt: &str, ) -> anyhow::Result> where diff --git a/src/interactive/state.rs b/src/interactive/state.rs index ca84d13..fdaf20b 100644 --- a/src/interactive/state.rs +++ b/src/interactive/state.rs @@ -1,4 +1,4 @@ -use tui::widgets::ListState; +use ratatui::widgets::ListState; /// Handles the state of the application /// Provides functions `next`, `previous` etc. @@ -83,17 +83,22 @@ impl<'a, T: Clone> StatefulList<'a, T> { /// app.push_filter('s'); /// assert_eq!(items[2], app.get_selected().unwrap()); /// ``` -pub struct AppState<'a> { +pub struct ListView<'a> { pub items: StatefulList<'a, &'a str>, pub filter: String, + pub help: Option<&'a str>, } -impl<'a> AppState<'a> { - pub fn new(items: &'a [&'a str]) -> AppState<'a> { +impl<'a> ListView<'a> { + pub fn new(items: &'a [&'a str], help: Option<&'a str>) -> ListView<'a> { let filter = String::from(""); let mut items = StatefulList::with_items(items); items.next(); - AppState { items, filter } + ListView { + items, + filter, + help, + } } /// pushes an ASCII character to the filter @@ -141,7 +146,7 @@ impl<'a> AppState<'a> { #[cfg(test)] mod tests { - use super::AppState; + use super::ListView; fn get_item_list() -> &'static [&'static str] { &["eka", "toka", "kolmas"] @@ -151,7 +156,7 @@ mod tests { fn app_state_new() { let items = get_item_list(); - let app = AppState::new(items); + let app = ListView::new(items, None); assert_eq!(app.items.items, items); assert_eq!(app.items.displayed, items); @@ -162,7 +167,7 @@ mod tests { fn app_select_next() { let items = get_item_list(); - let mut app = AppState::new(items); + let mut app = ListView::new(items, None); //assert_eq!(items[0], items[app.items.get_current().unwrap()]); assert_eq!(items[0], app.get_selected().unwrap()); @@ -176,7 +181,7 @@ mod tests { fn app_test_filter_push_pop() { let items = get_item_list(); - let mut app = AppState::new(items); + let mut app = ListView::new(items, None); app.push_filter('s'); assert_eq!(items[2], app.get_selected().unwrap()); diff --git a/src/main.rs b/src/main.rs index ec3eaec..24ff21e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -23,5 +23,13 @@ fn main() { }; let mut output = StandardStream::stderr(color); let mut io = Io::new(&mut output, &mut stdin); + + // we ignore errors here because it's not a big deal if these operations fail + let _ = ctrlc::set_handler(move || { + // if the user presses Ctrl+C during raw mode, it can mess up the terminal, + // so we disable it here + let _ = crossterm::terminal::disable_raw_mode(); + }); + tmc::run(cli, &mut io); }