diff options
| -rw-r--r-- | CHANGELOG.md | 14 | ||||
| -rw-r--r-- | Cargo.lock | 332 | ||||
| -rw-r--r-- | Cargo.toml | 7 | ||||
| -rw-r--r-- | clippy.toml | 16 | ||||
| -rw-r--r-- | exercises/18_iterators/iterators3.rs | 2 | ||||
| -rw-r--r-- | exercises/README.md | 2 | ||||
| -rw-r--r-- | rustlings-macros/Cargo.toml | 2 | ||||
| -rw-r--r-- | rustlings-macros/src/lib.rs | 2 | ||||
| -rw-r--r-- | src/app_state.rs | 12 | ||||
| -rw-r--r-- | src/cargo_toml.rs | 9 | ||||
| -rw-r--r-- | src/dev/check.rs | 50 | ||||
| -rw-r--r-- | src/dev/new.rs | 3 | ||||
| -rw-r--r-- | src/embedded.rs | 2 | ||||
| -rw-r--r-- | src/exercise.rs | 38 | ||||
| -rw-r--r-- | src/info_file.rs | 4 | ||||
| -rw-r--r-- | src/init.rs | 38 | ||||
| -rw-r--r-- | src/list/state.rs | 8 | ||||
| -rw-r--r-- | src/main.rs | 8 | ||||
| -rw-r--r-- | src/run.rs | 6 | ||||
| -rw-r--r-- | src/term.rs | 60 | ||||
| -rw-r--r-- | src/watch/state.rs | 4 |
21 files changed, 387 insertions, 232 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md index c1dbb42..32d9510 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,9 +1,23 @@ ## Unreleased +## 6.5.0 (2025-08-21) + +### Added + +- Check that Clippy is installed before initialization + ### Changed - Upgrade to Rust edition 2024 - Raise the minimum supported Rust version to `1.87` +- Don't follow symlinks in the file watcher +- `dev new`: Don't add `.rustlings-state.txt` to `.gitignore` + +### Fixed + +- Fix file links in VS Code +- Fix error printing when the progress bar is shown +- `dev check`: Don't check formatting if there are no solution files ## 6.4.0 (2024-11-11) @@ -4,9 +4,9 @@ version = 4 [[package]] name = "anstream" -version = "0.6.18" +version = "0.6.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8acc5369981196006228e28809f761875c0327210a891e941f4c683b3a99529b" +checksum = "3ae563653d1938f79b1ab1b5e668c87c76a9930414574a6583a7b7e11a8e6192" dependencies = [ "anstyle", "anstyle-parse", @@ -19,50 +19,50 @@ dependencies = [ [[package]] name = "anstyle" -version = "1.0.10" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9" +checksum = "862ed96ca487e809f1c8e5a8447f6ee2cf102f846893800b20cebdf541fc6bbd" [[package]] name = "anstyle-parse" -version = "0.2.6" +version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9" +checksum = "4e7644824f0aa2c7b9384579234ef10eb7efb6a0deb83f9630a49594dd9c15c2" dependencies = [ "utf8parse", ] [[package]] name = "anstyle-query" -version = "1.1.2" +version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c" +checksum = "9e231f6134f61b71076a3eab506c379d4f36122f2af15a9ff04415ea4c3339e2" dependencies = [ - "windows-sys", + "windows-sys 0.60.2", ] [[package]] name = "anstyle-wincon" -version = "3.0.8" +version = "3.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6680de5231bd6ee4c6191b8a1325daa282b415391ec9d3a37bd34f2060dc73fa" +checksum = "3e0633414522a32ffaac8ac6cc8f748e090c5717661fddeea04219e2344f5f2a" dependencies = [ "anstyle", "once_cell_polyfill", - "windows-sys", + "windows-sys 0.60.2", ] [[package]] name = "anyhow" -version = "1.0.98" +version = "1.0.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487" +checksum = "b0674a1ddeecb70197781e945de4b3b8ffb61fa939a5597bcf48503737663100" [[package]] name = "autocfg" -version = "1.4.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" +checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" [[package]] name = "bitflags" @@ -72,21 +72,21 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.9.1" +version = "2.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967" +checksum = "6a65b545ab31d687cff52899d4890855fec459eb6afe0da6417b8a18da87aa29" [[package]] name = "cfg-if" -version = "1.0.0" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +checksum = "2fd1289c04a9ea8cb22300a459a72a385d7c73d3259e2ed7dcb2af674838cfa9" [[package]] name = "clap" -version = "4.5.39" +version = "4.5.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd60e63e9be68e5fb56422e397cf9baddded06dae1d2e523401542383bc72a9f" +checksum = "1fc0e74a703892159f5ae7d3aac52c8e6c392f5ae5f359c70b5881d60aaac318" dependencies = [ "clap_builder", "clap_derive", @@ -94,9 +94,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.39" +version = "4.5.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89cc6392a1f72bbeb820d71f32108f61fdaf18bc526e1d23954168a67759ef51" +checksum = "b3e7f4214277f3c7aa526a59dd3fbe306a370daee1f8b7b8c987069cd8e888a8" dependencies = [ "anstream", "anstyle", @@ -106,9 +106,9 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.5.32" +version = "4.5.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09176aae279615badda0765c0c0b3f6ed53f4709118af73cf4655d85d1530cd7" +checksum = "14cb31bb0a7d536caef2639baa7fad459e15c3144efefa6dbd1c84562c4739f6" dependencies = [ "heck", "proc-macro2", @@ -118,15 +118,15 @@ dependencies = [ [[package]] name = "clap_lex" -version = "0.7.4" +version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6" +checksum = "b94f61472cee1439c0b966b47e3aca9ae07e45d070759512cd390ea2bebc6675" [[package]] name = "colorchoice" -version = "1.0.3" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990" +checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75" [[package]] name = "crossterm" @@ -134,7 +134,7 @@ version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d8b9f2e4c67f833b660cdb0a3523065869fb35570177239812ed4c905aeff87b" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.2", "crossterm_winapi", "document-features", "mio", @@ -171,12 +171,12 @@ checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" [[package]] name = "errno" -version = "0.3.12" +version = "0.3.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cea14ef9355e3beab063703aa9dab15afd25f0667c341310c1e5274bb1d0da18" +checksum = "778e2ac28f6c47af28e4907f13ffd1e1ddbd400980a9abd7c8df189bf578a5ad" dependencies = [ "libc", - "windows-sys", + "windows-sys 0.60.2", ] [[package]] @@ -186,18 +186,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" [[package]] -name = "filetime" -version = "0.2.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35c0522e981e68cbfa8c3f978441a5f34b30b96e146b33cd3359176b50fe8586" -dependencies = [ - "cfg-if", - "libc", - "libredox", - "windows-sys", -] - -[[package]] name = "fsevent-sys" version = "4.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -220,9 +208,9 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.15.3" +version = "0.15.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84b26c544d002229e640969970a2e74021aadf6e2f96372b9c58eff97de08eb3" +checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" [[package]] name = "heck" @@ -232,9 +220,9 @@ checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" [[package]] name = "indexmap" -version = "2.9.0" +version = "2.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cea70ddb795996207ad57735b50c5982d8844f38ba9ee5f1aedcfb708a2aa11e" +checksum = "fe4cd85333e22411419a0bcae1297d25e58c9443848b11dc6a86fefe8c78a661" dependencies = [ "equivalent", "hashbrown", @@ -246,7 +234,7 @@ version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f37dccff2791ab604f9babef0ba14fbe0be30bd368dc541e2b08d07c8aa908f3" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.2", "inotify-sys", "libc", ] @@ -294,20 +282,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.172" +version = "0.2.175" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa" - -[[package]] -name = "libredox" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" -dependencies = [ - "bitflags 2.9.1", - "libc", - "redox_syscall", -] +checksum = "6a82ae493e598baaea5209805c49bbf2ea7de956d50d7da0da1164f9c6d28543" [[package]] name = "linux-raw-sys" @@ -317,9 +294,9 @@ checksum = "cd945864f07fe9f5371a27ad7b52a172b4b499999f1d97574c9fa68373937e12" [[package]] name = "litrs" -version = "0.4.1" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4ce301924b7887e9d637144fdade93f9dfff9b60981d4ac161db09720d39aa5" +checksum = "f5e54036fe321fd421e10d732f155734c4e4afd610dd556d9a82833ab3ee0bed" [[package]] name = "lock_api" @@ -339,9 +316,9 @@ checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" [[package]] name = "memchr" -version = "2.7.4" +version = "2.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" +checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0" [[package]] name = "mio" @@ -351,18 +328,17 @@ checksum = "78bed444cc8a2160f01cbcf811ef18cac863ad68ae8ca62092e8db51d51c761c" dependencies = [ "libc", "log", - "wasi 0.11.0+wasi-snapshot-preview1", - "windows-sys", + "wasi 0.11.1+wasi-snapshot-preview1", + "windows-sys 0.59.0", ] [[package]] name = "notify" -version = "8.0.0" +version = "8.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fee8403b3d66ac7b26aee6e40a897d85dc5ce26f44da36b8b73e987cc52e943" +checksum = "4d3d07927151ff8575b7087f245456e549fea62edf0ec4e565a5ee50c8402bc3" dependencies = [ - "bitflags 2.9.1", - "filetime", + "bitflags 2.9.2", "fsevent-sys", "inotify", "kqueue", @@ -371,7 +347,7 @@ dependencies = [ "mio", "notify-types", "walkdir", - "windows-sys", + "windows-sys 0.60.2", ] [[package]] @@ -412,14 +388,14 @@ dependencies = [ "libc", "redox_syscall", "smallvec", - "windows-targets", + "windows-targets 0.52.6", ] [[package]] name = "proc-macro2" -version = "1.0.95" +version = "1.0.101" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778" +checksum = "89ae43fd86e4158d6db51ad8e2b80f313af9cc74f5c0e03ccb87de09998732de" dependencies = [ "unicode-ident", ] @@ -435,30 +411,30 @@ dependencies = [ [[package]] name = "r-efi" -version = "5.2.0" +version = "5.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74765f6d916ee2faa39bc8e68e4f3ed8949b48cccdac59983d287a7cb71ce9c5" +checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" [[package]] name = "redox_syscall" -version = "0.5.12" +version = "0.5.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "928fca9cf2aa042393a8325b9ead81d2f0df4cb12e1e24cef072922ccd99c5af" +checksum = "5407465600fb0548f1442edf71dd20683c6ed326200ace4b1ef0763521bb3b77" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.2", ] [[package]] name = "rustix" -version = "1.0.7" +version = "1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c71e83d6afe7ff64890ec6b71d6a69bb8a610ab78ce364b3352876bb4c801266" +checksum = "11181fbabf243db407ef8df94a6ce0b2f9a733bd8be4ad02b4eda9602296cac8" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.2", "errno", "libc", "linux-raw-sys", - "windows-sys", + "windows-sys 0.60.2", ] [[package]] @@ -474,7 +450,7 @@ dependencies = [ "serde", "serde_json", "tempfile", - "toml_edit", + "toml", ] [[package]] @@ -483,7 +459,7 @@ version = "6.4.0" dependencies = [ "quote", "serde", - "toml_edit", + "toml", ] [[package]] @@ -529,9 +505,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.140" +version = "1.0.143" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373" +checksum = "d401abef1d108fbd9cbaebc3e46611f4b1021f714a0597a71f41ee463f5f4a5a" dependencies = [ "itoa", "memchr", @@ -541,9 +517,9 @@ dependencies = [ [[package]] name = "serde_spanned" -version = "0.6.8" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87607cb1398ed59d48732e575a4c28a7a8ebf2454b964fe3f224f2afc07909e1" +checksum = "40734c41988f7306bb04f0ecf60ec0f3f1caa34290e4e8ea471dcd3346483b83" dependencies = [ "serde", ] @@ -571,18 +547,18 @@ dependencies = [ [[package]] name = "signal-hook-registry" -version = "1.4.5" +version = "1.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9203b8055f63a2a00e2f593bb0510367fe707d7ff1e5c872de2f537b339e5410" +checksum = "b2a4719bff48cee6b39d12c020eeb490953ad2443b7055bd0b21fca26bd8c28b" dependencies = [ "libc", ] [[package]] name = "smallvec" -version = "1.15.0" +version = "1.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8917285742e9f3e1683f0a9c4e6b57960b7314d0b08d30d1ecd426713ee2eee9" +checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" [[package]] name = "strsim" @@ -592,9 +568,9 @@ checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" [[package]] name = "syn" -version = "2.0.101" +version = "2.0.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ce2b7fc941b3a24138a0a7cf8e858bfc6a992e7978a068a5c760deb0ed43caf" +checksum = "ede7c438028d4436d71104916910f5bb611972c5cfd7f89b8300a8186e6fada6" dependencies = [ "proc-macro2", "quote", @@ -603,40 +579,57 @@ dependencies = [ [[package]] name = "tempfile" -version = "3.20.0" +version = "3.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8a64e3985349f2441a1a9ef0b853f869006c3855f2cda6862a94d26ebb9d6a1" +checksum = "15b61f8f20e3a6f7e0649d825294eaf317edce30f82cf6026e7e4cb9222a7d1e" dependencies = [ "fastrand", "getrandom", "once_cell", "rustix", - "windows-sys", + "windows-sys 0.60.2", ] [[package]] -name = "toml_datetime" -version = "0.6.9" +name = "toml" +version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3da5db5a963e24bc68be8b17b6fa82814bb22ee8660f192bb182771d498f09a3" +checksum = "75129e1dc5000bfbaa9fee9d1b21f974f9fbad9daec557a521ee6e080825f6e8" dependencies = [ + "indexmap", "serde", + "serde_spanned", + "toml_datetime", + "toml_parser", + "toml_writer", + "winnow", ] [[package]] -name = "toml_edit" -version = "0.22.26" +name = "toml_datetime" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "310068873db2c5b3e7659d2cc35d21855dbafa50d1ce336397c666e3cb08137e" +checksum = "bade1c3e902f58d73d3f294cd7f20391c1cb2fbcb643b73566bc773971df91e3" dependencies = [ - "indexmap", "serde", - "serde_spanned", - "toml_datetime", +] + +[[package]] +name = "toml_parser" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b551886f449aa90d4fe2bdaa9f4a2577ad2dde302c61ecf262d80b116db95c10" +dependencies = [ "winnow", ] [[package]] +name = "toml_writer" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcc842091f2def52017664b53082ecbbeb5c7731092bad69d2c63050401dfd64" + +[[package]] name = "unicode-ident" version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -660,9 +653,9 @@ dependencies = [ [[package]] name = "wasi" -version = "0.11.0+wasi-snapshot-preview1" +version = "0.11.1+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" [[package]] name = "wasi" @@ -691,11 +684,11 @@ checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] name = "winapi-util" -version = "0.1.9" +version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" +checksum = "0978bf7171b3d90bac376700cb56d606feb40f251a475a5d6634613564460b22" dependencies = [ - "windows-sys", + "windows-sys 0.60.2", ] [[package]] @@ -705,12 +698,27 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] +name = "windows-link" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a" + +[[package]] name = "windows-sys" version = "0.59.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" dependencies = [ - "windows-targets", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" +dependencies = [ + "windows-targets 0.53.3", ] [[package]] @@ -719,14 +727,31 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_gnullvm", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", + "windows_i686_gnullvm 0.52.6", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", +] + +[[package]] +name = "windows-targets" +version = "0.53.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5fe6031c4041849d7c496a8ded650796e7b6ecc19df1a431c1a363342e5dc91" +dependencies = [ + "windows-link", + "windows_aarch64_gnullvm 0.53.0", + "windows_aarch64_msvc 0.53.0", + "windows_i686_gnu 0.53.0", + "windows_i686_gnullvm 0.53.0", + "windows_i686_msvc 0.53.0", + "windows_x86_64_gnu 0.53.0", + "windows_x86_64_gnullvm 0.53.0", + "windows_x86_64_msvc 0.53.0", ] [[package]] @@ -736,55 +761,100 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" [[package]] +name = "windows_aarch64_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86b8d5f90ddd19cb4a147a5fa63ca848db3df085e25fee3cc10b39b6eebae764" + +[[package]] name = "windows_aarch64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" [[package]] +name = "windows_aarch64_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7651a1f62a11b8cbd5e0d42526e55f2c99886c77e007179efff86c2b137e66c" + +[[package]] name = "windows_i686_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" [[package]] +name = "windows_i686_gnu" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1dc67659d35f387f5f6c479dc4e28f1d4bb90ddd1a5d3da2e5d97b42d6272c3" + +[[package]] name = "windows_i686_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" [[package]] +name = "windows_i686_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ce6ccbdedbf6d6354471319e781c0dfef054c81fbc7cf83f338a4296c0cae11" + +[[package]] name = "windows_i686_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" [[package]] +name = "windows_i686_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "581fee95406bb13382d2f65cd4a908ca7b1e4c2f1917f143ba16efe98a589b5d" + +[[package]] name = "windows_x86_64_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" [[package]] +name = "windows_x86_64_gnu" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e55b5ac9ea33f2fc1716d1742db15574fd6fc8dadc51caab1c16a3d3b4190ba" + +[[package]] name = "windows_x86_64_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" [[package]] +name = "windows_x86_64_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a6e035dd0599267ce1ee132e51c27dd29437f63325753051e71dd9e42406c57" + +[[package]] name = "windows_x86_64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] +name = "windows_x86_64_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486" + +[[package]] name = "winnow" -version = "0.7.10" +version = "0.7.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c06928c8748d81b05c9be96aad92e1b6ff01833332f281e8cfca3be4b35fc9ec" -dependencies = [ - "memchr", -] +checksum = "f3edebf492c8125044983378ecb5766203ad3b4c2f7a922bd7dd207f6d443e95" [[package]] name = "wit-bindgen-rt" @@ -792,5 +862,5 @@ version = "0.39.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.2", ] @@ -1,5 +1,4 @@ [workspace] -resolver = "2" exclude = [ "tests/test_exercises", "dev", @@ -20,7 +19,7 @@ rust-version = "1.87" [workspace.dependencies] serde = { version = "1.0", features = ["derive"] } -toml_edit = { version = "0.22", default-features = false, features = ["parse", "serde"] } +toml = { version = "0.9", default-features = false, features = ["std", "parse", "serde"] } [package] name = "rustlings" @@ -53,13 +52,13 @@ notify = "8.0" rustlings-macros = { path = "rustlings-macros", version = "=6.4.0" } serde_json = "1.0" serde.workspace = true -toml_edit.workspace = true +toml.workspace = true [target.'cfg(not(windows))'.dependencies] rustix = { version = "1.0", default-features = false, features = ["std", "stdio", "termios"] } [dev-dependencies] -tempfile = "3.19" +tempfile = "3.21" [profile.release] panic = "abort" diff --git a/clippy.toml b/clippy.toml index afc9253..89b0a88 100644 --- a/clippy.toml +++ b/clippy.toml @@ -1,15 +1,11 @@ disallowed-types = [ - # Inefficient. Use `.queue(…)` instead. - "crossterm::style::Stylize", - "crossterm::style::styled_content::StyledContent", + { path = "crossterm::style::Stylize", reason = "inefficient, use `.queue(…)` instead" }, + { path = "crossterm::style::styled_content::StyledContent", reason = "inefficient, use `.queue(…)` instead" }, ] disallowed-methods = [ - # Inefficient. Use `.queue(…)` instead. - "crossterm::style::style", - # Use `thread::Builder::spawn` instead and handle the error. - "std::thread::spawn", - "std::thread::Scope::spawn", - # Return `ExitCode` instead. - "std::process::exit", + { path = "crossterm::style::style", reason = "inefficient, use `.queue(…)` instead" }, + { path = "std::thread::spawn", replacement = "std::thread::Builder::spawn", reason = "handle the error" }, + { path = "std::thread::Scope::spawn", replacement = "std::thread::Builder::spawn", reason = "handle the error" }, + { path = "std::process::exit", replacement = "std::process::ExitCode" }, ] diff --git a/exercises/18_iterators/iterators3.rs b/exercises/18_iterators/iterators3.rs index 6b1eca1..dce0905 100644 --- a/exercises/18_iterators/iterators3.rs +++ b/exercises/18_iterators/iterators3.rs @@ -39,6 +39,8 @@ mod tests { #[test] fn test_success() { assert_eq!(divide(81, 9), Ok(9)); + assert_eq!(divide(81, -1), Ok(-81)); + assert_eq!(divide(i64::MIN, i64::MIN), Ok(1)); } #[test] diff --git a/exercises/README.md b/exercises/README.md index 237f2f1..86b3591 100644 --- a/exercises/README.md +++ b/exercises/README.md @@ -22,6 +22,6 @@ | iterators | §13.2-4 | | smart_pointers | §15, §16.3 | | threads | §16.1-3 | -| macros | §19.5 | +| macros | §20.5 | | clippy | §21.4 | | conversions | n/a | diff --git a/rustlings-macros/Cargo.toml b/rustlings-macros/Cargo.toml index 1bf6d1b..5df648b 100644 --- a/rustlings-macros/Cargo.toml +++ b/rustlings-macros/Cargo.toml @@ -18,7 +18,7 @@ proc-macro = true [dependencies] quote = "1.0" serde.workspace = true -toml_edit.workspace = true +toml.workspace = true [lints] workspace = true diff --git a/rustlings-macros/src/lib.rs b/rustlings-macros/src/lib.rs index 6c6067b..b20c6f1 100644 --- a/rustlings-macros/src/lib.rs +++ b/rustlings-macros/src/lib.rs @@ -16,7 +16,7 @@ struct InfoFile { #[proc_macro] pub fn include_files(_: TokenStream) -> TokenStream { let info_file = include_str!("../info.toml"); - let exercises = toml_edit::de::from_str::<InfoFile>(info_file) + let exercises = toml::de::from_str::<InfoFile>(info_file) .expect("Failed to parse `info.toml`") .exercises; diff --git a/src/app_state.rs b/src/app_state.rs index f3f3481..d654d04 100644 --- a/src/app_state.rs +++ b/src/app_state.rs @@ -60,8 +60,7 @@ pub struct AppState { file_buf: Vec<u8>, official_exercises: bool, cmd_runner: CmdRunner, - // Running in VS Code. - vs_code: bool, + emit_file_links: bool, } impl AppState { @@ -181,7 +180,8 @@ impl AppState { file_buf, official_exercises: !Path::new("info.toml").exists(), cmd_runner, - vs_code: env::var_os("TERM_PROGRAM").is_some_and(|v| v == "vscode"), + // VS Code has its own file link handling + emit_file_links: env::var_os("TERM_PROGRAM").is_none_or(|v| v != "vscode"), }; Ok((slf, state_file_status)) @@ -218,8 +218,8 @@ impl AppState { } #[inline] - pub fn vs_code(&self) -> bool { - self.vs_code + pub fn emit_file_links(&self) -> bool { + self.emit_file_links } // Write the state file. @@ -621,7 +621,7 @@ mod tests { file_buf: Vec::new(), official_exercises: true, cmd_runner: CmdRunner::build().unwrap(), - vs_code: false, + emit_file_links: true, }; let mut assert = |done: [bool; 3], expected: [Option<usize>; 3]| { diff --git a/src/cargo_toml.rs b/src/cargo_toml.rs index e966809..ce0dfd0 100644 --- a/src/cargo_toml.rs +++ b/src/cargo_toml.rs @@ -134,7 +134,14 @@ mod tests { ); assert_eq!( - updated_cargo_toml(&exercise_infos, "abc\nbin = [xxx]\n123", b"../").unwrap(), + updated_cargo_toml( + &exercise_infos, + "abc\n\ + bin = [xxx]\n\ + 123", + b"../" + ) + .unwrap(), br#"abc bin = [ { name = "1", path = "../exercises/1.rs" }, diff --git a/src/dev/check.rs b/src/dev/check.rs index 9cde7f2..f711106 100644 --- a/src/dev/check.rs +++ b/src/dev/check.rs @@ -15,6 +15,7 @@ use crate::{ cmd::CmdRunner, exercise::{OUTPUT_CAPACITY, RunnableExercise}, info_file::{ExerciseInfo, InfoFile}, + term::ProgressCounter, }; const MAX_N_EXERCISES: usize = 999; @@ -105,13 +106,15 @@ fn check_info_file_exercises(info_file: &InfoFile) -> Result<HashSet<PathBuf>> { if !file_buf.contains("fn main()") { bail!( - "The `main` function is missing in the file `{path}`.\nCreate at least an empty `main` function to avoid language server errors" + "The `main` function is missing in the file `{path}`.\n\ + Create at least an empty `main` function to avoid language server errors" ); } if !file_buf.contains("// TODO") { bail!( - "Didn't find any `// TODO` comment in the file `{path}`.\nYou need to have at least one such comment to guide the user." + "Didn't find any `// TODO` comment in the file `{path}`.\n\ + You need to have at least one such comment to guide the user." ); } @@ -217,10 +220,7 @@ fn check_exercises_unsolved( .collect::<Result<Vec<_>, _>>() .context("Failed to spawn a thread to check if an exercise is already solved")?; - let n_handles = handles.len(); - write!(stdout, "Progress: 0/{n_handles}")?; - stdout.flush()?; - let mut handle_num = 1; + let mut progress_counter = ProgressCounter::new(&mut stdout, handles.len())?; for (exercise_name, handle) in handles { let Ok(result) = handle.join() else { @@ -229,17 +229,17 @@ fn check_exercises_unsolved( match result { Ok(true) => { - bail!("The exercise {exercise_name} is already solved.\n{SKIP_CHECK_UNSOLVED_HINT}",) + bail!( + "The exercise {exercise_name} is already solved.\n\ + {SKIP_CHECK_UNSOLVED_HINT}", + ) } Ok(false) => (), Err(e) => return Err(e), } - write!(stdout, "\rProgress: {handle_num}/{n_handles}")?; - stdout.flush()?; - handle_num += 1; + progress_counter.increment()?; } - stdout.write_all(b"\n")?; Ok(()) } @@ -247,10 +247,12 @@ fn check_exercises_unsolved( fn check_exercises(info_file: &'static InfoFile, cmd_runner: &'static CmdRunner) -> Result<()> { match info_file.format_version.cmp(&CURRENT_FORMAT_VERSION) { Ordering::Less => bail!( - "`format_version` < {CURRENT_FORMAT_VERSION} (supported version)\nPlease migrate to the latest format version" + "`format_version` < {CURRENT_FORMAT_VERSION} (supported version)\n\ + Please migrate to the latest format version" ), Ordering::Greater => bail!( - "`format_version` > {CURRENT_FORMAT_VERSION} (supported version)\nTry updating the Rustlings program" + "`format_version` > {CURRENT_FORMAT_VERSION} (supported version)\n\ + Try updating the Rustlings program" ), Ordering::Equal => (), } @@ -318,10 +320,7 @@ fn check_solutions( .arg("always") .stdin(Stdio::null()); - let n_handles = handles.len(); - write!(stdout, "Progress: 0/{n_handles}")?; - stdout.flush()?; - let mut handle_num = 1; + let mut progress_counter = ProgressCounter::new(&mut stdout, handles.len())?; for (exercise_info, handle) in info_file.exercises.iter().zip(handles) { let Ok(check_result) = handle.join() else { @@ -338,7 +337,7 @@ fn check_solutions( } SolutionCheck::MissingOptional => (), SolutionCheck::RunFailure { output } => { - stdout.write_all(b"\n\n")?; + drop(progress_counter); stdout.write_all(&output)?; bail!( "Running the solution of the exercise {} failed with the error above", @@ -348,22 +347,21 @@ fn check_solutions( SolutionCheck::Err(e) => return Err(e), } - write!(stdout, "\rProgress: {handle_num}/{n_handles}")?; - stdout.flush()?; - handle_num += 1; + progress_counter.increment()?; } - stdout.write_all(b"\n")?; + let n_solutions = sol_paths.len(); let handle = thread::Builder::new() .spawn(move || check_unexpected_files("solutions", &sol_paths)) .context( "Failed to spawn a thread to check for unexpected files in the solutions directory", )?; - if !fmt_cmd - .status() - .context("Failed to run `rustfmt` on all solution files")? - .success() + if n_solutions > 0 + && !fmt_cmd + .status() + .context("Failed to run `rustfmt` on all solution files")? + .success() { bail!("Some solutions aren't formatted. Run `rustfmt` on them"); } diff --git a/src/dev/new.rs b/src/dev/new.rs index 883b6fa..7c72a6b 100644 --- a/src/dev/new.rs +++ b/src/dev/new.rs @@ -78,8 +78,7 @@ pub fn new(path: &Path, no_git: bool) -> Result<()> { Ok(()) } -pub const GITIGNORE: &[u8] = b".rustlings-state.txt -Cargo.lock +pub const GITIGNORE: &[u8] = b"Cargo.lock target/ .vscode/ !.vscode/extensions.json diff --git a/src/embedded.rs b/src/embedded.rs index 51a14b6..88c1fb0 100644 --- a/src/embedded.rs +++ b/src/embedded.rs @@ -152,7 +152,7 @@ mod tests { #[test] fn dirs() { - let exercises = toml_edit::de::from_str::<InfoFile>(EMBEDDED_FILES.info_file) + let exercises = toml::de::from_str::<InfoFile>(EMBEDDED_FILES.info_file) .expect("Failed to parse `info.toml`") .exercises; diff --git a/src/exercise.rs b/src/exercise.rs index fdfbc4f..6f517be 100644 --- a/src/exercise.rs +++ b/src/exercise.rs @@ -7,22 +7,28 @@ use std::io::{self, StdoutLock, Write}; use crate::{ cmd::CmdRunner, - term::{self, CountedWrite, terminal_file_link, write_ansi}, + term::{self, CountedWrite, file_path, terminal_file_link, write_ansi}, }; /// The initial capacity of the output buffer. pub const OUTPUT_CAPACITY: usize = 1 << 14; -pub fn solution_link_line(stdout: &mut StdoutLock, solution_path: &str) -> io::Result<()> { +pub fn solution_link_line( + stdout: &mut StdoutLock, + solution_path: &str, + emit_file_links: bool, +) -> io::Result<()> { stdout.queue(SetAttribute(Attribute::Bold))?; stdout.write_all(b"Solution")?; stdout.queue(ResetColor)?; stdout.write_all(b" for comparison: ")?; - if let Some(canonical_path) = term::canonicalize(solution_path) { - terminal_file_link(stdout, solution_path, &canonical_path, Color::Cyan)?; - } else { - stdout.write_all(solution_path.as_bytes())?; - } + file_path(stdout, Color::Cyan, |writer| { + if emit_file_links && let Some(canonical_path) = term::canonicalize(solution_path) { + terminal_file_link(writer, solution_path, &canonical_path) + } else { + writer.stdout().write_all(solution_path.as_bytes()) + } + })?; stdout.write_all(b"\n") } @@ -72,12 +78,18 @@ pub struct Exercise { } impl Exercise { - pub fn terminal_file_link<'a>(&self, writer: &mut impl CountedWrite<'a>) -> io::Result<()> { - if let Some(canonical_path) = self.canonical_path.as_deref() { - return terminal_file_link(writer, self.path, canonical_path, Color::Blue); - } - - writer.write_str(self.path) + pub fn terminal_file_link<'a>( + &self, + writer: &mut impl CountedWrite<'a>, + emit_file_links: bool, + ) -> io::Result<()> { + file_path(writer, Color::Blue, |writer| { + if emit_file_links && let Some(canonical_path) = self.canonical_path.as_deref() { + terminal_file_link(writer, self.path, canonical_path) + } else { + writer.write_str(self.path) + } + }) } } diff --git a/src/info_file.rs b/src/info_file.rs index 634bece..04e5d64 100644 --- a/src/info_file.rs +++ b/src/info_file.rs @@ -95,11 +95,11 @@ impl InfoFile { pub fn parse() -> Result<Self> { // Read a local `info.toml` if it exists. let slf = match fs::read_to_string("info.toml") { - Ok(file_content) => toml_edit::de::from_str::<Self>(&file_content) + Ok(file_content) => toml::de::from_str::<Self>(&file_content) .context("Failed to parse the `info.toml` file")?, Err(e) => { if e.kind() == ErrorKind::NotFound { - return toml_edit::de::from_str(EMBEDDED_FILES.info_file) + return toml::de::from_str(EMBEDDED_FILES.info_file) .context("Failed to parse the embedded `info.toml` file"); } diff --git a/src/init.rs b/src/init.rs index a60fba7..68011ed 100644 --- a/src/init.rs +++ b/src/init.rs @@ -35,7 +35,27 @@ pub fn init() -> Result<()> { .stdin(Stdio::null()) .stderr(Stdio::null()) .output() - .context(CARGO_LOCATE_PROJECT_ERR)?; + .context( + "Failed to run the command `cargo locate-project …`\n\ + Did you already install Rust?\n\ + Try running `cargo --version` to diagnose the problem.", + )?; + + if !Command::new("cargo") + .arg("clippy") + .arg("--version") + .stdin(Stdio::null()) + .stdout(Stdio::null()) + .stderr(Stdio::null()) + .status() + .context("Failed to run the command `cargo clippy --version`")? + .success() + { + bail!( + "Clippy, the official Rust linter, is missing.\n\ + Please install it first before initializing Rustlings." + ) + } let mut stdout = io::stdout().lock(); let mut init_git = true; @@ -58,11 +78,13 @@ pub fn init() -> Result<()> { && !workspace_manifest_content.contains("workspace.") { bail!( - "The current directory is already part of a Cargo project.\nPlease initialize Rustlings in a different directory" + "The current directory is already part of a Cargo project.\n\ + Please initialize Rustlings in a different directory" ); } - stdout.write_all(b"This command will create the directory `rustlings/` as a member of this Cargo workspace.\nPress ENTER to continue ")?; + stdout.write_all(b"This command will create the directory `rustlings/` as a member of this Cargo workspace.\n\ + Press ENTER to continue ")?; press_enter_prompt(&mut stdout)?; // Make sure "rustlings" is added to `workspace.members` by making @@ -78,7 +100,8 @@ pub fn init() -> Result<()> { .status()?; if !status.success() { bail!( - "Failed to initialize a new Cargo workspace member.\nPlease initialize Rustlings in a different directory" + "Failed to initialize a new Cargo workspace member.\n\ + Please initialize Rustlings in a different directory" ); } @@ -87,7 +110,8 @@ pub fn init() -> Result<()> { .context("Failed to remove the temporary directory `rustlings/`")?; init_git = false; } else { - stdout.write_all(b"This command will create the directory `rustlings/` which will contain the exercises.\nPress ENTER to continue ")?; + stdout.write_all(b"This command will create the directory `rustlings/` which will contain the exercises.\n\ + Press ENTER to continue ")?; press_enter_prompt(&mut stdout)?; } @@ -166,10 +190,6 @@ pub fn init() -> Result<()> { Ok(()) } -const CARGO_LOCATE_PROJECT_ERR: &str = "Failed to run the command `cargo locate-project …` -Did you already install Rust? -Try running `cargo --version` to diagnose the problem."; - const INIT_SOLUTION_FILE: &[u8] = b"fn main() { // DON'T EDIT THIS SOLUTION FILE! // It will be automatically filled after you finish the exercise. diff --git a/src/list/state.rs b/src/list/state.rs index ae65ec2..50d06be 100644 --- a/src/list/state.rs +++ b/src/list/state.rs @@ -186,13 +186,7 @@ impl<'a> ListState<'a> { writer.write_ascii(&self.name_col_padding[exercise.name.len()..])?; - // The list links aren't shown correctly in VS Code on Windows. - // But VS Code shows its own links anyway. - if self.app_state.vs_code() { - writer.write_str(exercise.path)?; - } else { - exercise.terminal_file_link(&mut writer)?; - } + exercise.terminal_file_link(&mut writer, self.app_state.emit_file_links())?; writer.write_ascii(&self.path_col_padding[exercise.path.len()..])?; diff --git a/src/main.rs b/src/main.rs index bce2593..ffd2dfa 100644 --- a/src/main.rs +++ b/src/main.rs @@ -104,7 +104,11 @@ fn main() -> Result<ExitCode> { clear_terminal(&mut stdout)?; let welcome_message = welcome_message.trim_ascii(); - write!(stdout, "{welcome_message}\n\nPress ENTER to continue ")?; + write!( + stdout, + "{welcome_message}\n\n\ + Press ENTER to continue " + )?; press_enter_prompt(&mut stdout)?; clear_terminal(&mut stdout)?; // Flush to be able to show errors occurring before printing a newline to stdout. @@ -163,7 +167,7 @@ fn main() -> Result<ExitCode> { } app_state .current_exercise() - .terminal_file_link(&mut stdout)?; + .terminal_file_link(&mut stdout, app_state.emit_file_links())?; stdout.write_all(b"\n")?; return Ok(ExitCode::FAILURE); @@ -27,7 +27,7 @@ pub fn run(app_state: &mut AppState) -> Result<ExitCode> { stdout.write_all(b"Ran ")?; app_state .current_exercise() - .terminal_file_link(&mut stdout)?; + .terminal_file_link(&mut stdout, app_state.emit_file_links())?; stdout.write_all(b" with errors\n")?; return Ok(ExitCode::FAILURE); @@ -41,7 +41,7 @@ pub fn run(app_state: &mut AppState) -> Result<ExitCode> { if let Some(solution_path) = app_state.current_solution_path()? { stdout.write_all(b"\n")?; - solution_link_line(&mut stdout, &solution_path)?; + solution_link_line(&mut stdout, &solution_path, app_state.emit_file_links())?; stdout.write_all(b"\n")?; } @@ -50,7 +50,7 @@ pub fn run(app_state: &mut AppState) -> Result<ExitCode> { stdout.write_all(b"Next exercise: ")?; app_state .current_exercise() - .terminal_file_link(&mut stdout)?; + .terminal_file_link(&mut stdout, app_state.emit_file_links())?; stdout.write_all(b"\n")?; } ExercisesProgress::AllDone => (), diff --git a/src/term.rs b/src/term.rs index 1e08c84..3d149b3 100644 --- a/src/term.rs +++ b/src/term.rs @@ -160,6 +160,37 @@ impl<'a, 'lock> CheckProgressVisualizer<'a, 'lock> { } } +pub struct ProgressCounter<'a, 'lock> { + stdout: &'a mut StdoutLock<'lock>, + total: usize, + counter: usize, +} + +impl<'a, 'lock> ProgressCounter<'a, 'lock> { + pub fn new(stdout: &'a mut StdoutLock<'lock>, total: usize) -> io::Result<Self> { + write!(stdout, "Progress: 0/{total}")?; + stdout.flush()?; + + Ok(Self { + stdout, + total, + counter: 0, + }) + } + + pub fn increment(&mut self) -> io::Result<()> { + self.counter += 1; + write!(self.stdout, "\rProgress: {}/{}", self.counter, self.total)?; + self.stdout.flush() + } +} + +impl Drop for ProgressCounter<'_, '_> { + fn drop(&mut self) { + let _ = self.stdout.write_all(b"\n\n"); + } +} + pub fn progress_bar<'a>( writer: &mut impl CountedWrite<'a>, progress: u16, @@ -241,22 +272,18 @@ pub fn canonicalize(path: &str) -> Option<String> { }) } -pub fn terminal_file_link<'a>( - writer: &mut impl CountedWrite<'a>, - path: &str, - canonical_path: &str, +pub fn file_path<'a, W: CountedWrite<'a>>( + writer: &mut W, color: Color, + f: impl FnOnce(&mut W) -> io::Result<()>, ) -> io::Result<()> { writer .stdout() .queue(SetForegroundColor(color))? .queue(SetAttribute(Attribute::Underlined))?; - writer.stdout().write_all(b"\x1b]8;;file://")?; - writer.stdout().write_all(canonical_path.as_bytes())?; - writer.stdout().write_all(b"\x1b\\")?; - // Only this part is visible. - writer.write_str(path)?; - writer.stdout().write_all(b"\x1b]8;;\x1b\\")?; + + f(writer)?; + writer .stdout() .queue(SetForegroundColor(Color::Reset))? @@ -265,6 +292,19 @@ pub fn terminal_file_link<'a>( Ok(()) } +pub fn terminal_file_link<'a>( + writer: &mut impl CountedWrite<'a>, + path: &str, + canonical_path: &str, +) -> io::Result<()> { + writer.stdout().write_all(b"\x1b]8;;file://")?; + writer.stdout().write_all(canonical_path.as_bytes())?; + writer.stdout().write_all(b"\x1b\\")?; + // Only this part is visible. + writer.write_str(path)?; + writer.stdout().write_all(b"\x1b]8;;\x1b\\") +} + pub fn write_ansi(output: &mut Vec<u8>, command: impl Command) { struct FmtWriter<'a>(&'a mut Vec<u8>); diff --git a/src/watch/state.rs b/src/watch/state.rs index 2413bec..a92dd2d 100644 --- a/src/watch/state.rs +++ b/src/watch/state.rs @@ -233,7 +233,7 @@ impl<'a> WatchState<'a> { stdout.write_all(b"\n")?; if let DoneStatus::DoneWithSolution(solution_path) = &self.done_status { - solution_link_line(stdout, solution_path)?; + solution_link_line(stdout, solution_path, self.app_state.emit_file_links())?; } stdout.write_all( @@ -252,7 +252,7 @@ impl<'a> WatchState<'a> { stdout.write_all(b"\nCurrent exercise: ")?; self.app_state .current_exercise() - .terminal_file_link(stdout)?; + .terminal_file_link(stdout, self.app_state.emit_file_links())?; stdout.write_all(b"\n\n")?; self.show_prompt(stdout)?; |
