Merge remote-tracking branch 'origin/main'

This commit is contained in:
wznmickey 2024-03-28 00:11:19 +08:00
commit dd72429af4
26 changed files with 767 additions and 515 deletions

View file

@ -2559,6 +2559,132 @@
"contributions": [ "contributions": [
"content" "content"
] ]
},
{
"login": "AnonimAnonim2245",
"name": "Luca Plian",
"avatar_url": "https://avatars.githubusercontent.com/u/98339220?v=4",
"profile": "https://github.com/AnonimAnonim2245",
"contributions": [
"code"
]
},
{
"login": "reifenrath-dev",
"name": "René Reifenrath",
"avatar_url": "https://avatars.githubusercontent.com/u/18126097?v=4",
"profile": "https://reifenrath.dev/",
"contributions": [
"content"
]
},
{
"login": "peterneave",
"name": "Peter Neave",
"avatar_url": "https://avatars.githubusercontent.com/u/7982708?v=4",
"profile": "https://github.com/peterneave",
"contributions": [
"infra"
]
},
{
"login": "JanB1",
"name": "Jan",
"avatar_url": "https://avatars.githubusercontent.com/u/5552248?v=4",
"profile": "http://www.janb1.com",
"contributions": [
"content"
]
},
{
"login": "kylev",
"name": "Kyle VanderBeek",
"avatar_url": "https://avatars.githubusercontent.com/u/46888?v=4",
"profile": "http://www.kylev.com/",
"contributions": [
"infra"
]
},
{
"login": "pavedroad",
"name": "pavedroad",
"avatar_url": "https://avatars.githubusercontent.com/u/138004431?v=4",
"profile": "https://github.com/pavedroad",
"contributions": [
"content"
]
},
{
"login": "hyphena",
"name": "luna",
"avatar_url": "https://avatars.githubusercontent.com/u/26529488?v=4",
"profile": "https://github.com/hyphena",
"contributions": [
"content"
]
},
{
"login": "evanmiller2112",
"name": "Evan Miller",
"avatar_url": "https://avatars.githubusercontent.com/u/28488957?v=4",
"profile": "https://github.com/evanmiller2112",
"contributions": [
"content"
]
},
{
"login": "luvchurchill",
"name": "luvchurchill",
"avatar_url": "https://avatars.githubusercontent.com/u/46406654?v=4",
"profile": "https://github.com/luvchurchill",
"contributions": [
"code"
]
},
{
"login": "LeverImmy",
"name": "Ze-en Xiong",
"avatar_url": "https://avatars.githubusercontent.com/u/47663913?v=4",
"profile": "https://leverimmy.top/",
"contributions": [
"content"
]
},
{
"login": "parnavh",
"name": "Parnav Harinathan",
"avatar_url": "https://avatars.githubusercontent.com/u/45985534?v=4",
"profile": "https://github.com/parnavh",
"contributions": [
"content"
]
},
{
"login": "0Ahmed-0",
"name": "0Ahmed-0",
"avatar_url": "https://avatars.githubusercontent.com/u/111569638?v=4",
"profile": "https://github.com/0Ahmed-0",
"contributions": [
"content"
]
},
{
"login": "guizo792",
"name": "guizo792",
"avatar_url": "https://avatars.githubusercontent.com/u/95940388?v=4",
"profile": "https://github.com/guizo792",
"contributions": [
"content"
]
},
{
"login": "kazu728",
"name": "Kazuki Matsuo",
"avatar_url": "https://avatars.githubusercontent.com/u/34614358?v=4",
"profile": "https://github.com/kazu728",
"contributions": [
"code"
]
} }
], ],
"contributorsPerLine": 8, "contributorsPerLine": 8,

View file

@ -1,17 +1,8 @@
{ {
"image": "mcr.microsoft.com/devcontainers/universal:2-linux", "image": "mcr.microsoft.com/devcontainers/rust:1",
"waitFor": "onCreateCommand", "updateContentCommand": ["cargo", "build"],
"onCreateCommand": ".devcontainer/setup.sh", "postAttachCommand": ["rustlings", "watch"],
"updateContentCommand": "cargo build", "remoteEnv": {
"postCreateCommand": "", "PATH": "${containerEnv:PATH}:${containerWorkspaceFolder}/target/debug"
"postAttachCommand": {
"server": "rustlings watch"
},
"customizations": {
"vscode": {
"extensions": [
"rust-lang.rust-analyzer"
]
}
} }
} }

View file

@ -1,7 +0,0 @@
#!/bin/bash
curl https://sh.rustup.rs -sSf | sh -s -- -y
# Update current shell environment variables after install to find rustup
. "$HOME/.cargo/env"
rustup install stable
bash install.sh

2
.gitattributes vendored Normal file
View file

@ -0,0 +1,2 @@
* text=auto
*.sh text eol=lf

View file

@ -362,6 +362,22 @@ authors.
</tr> </tr>
<tr> <tr>
<td align="center" valign="top" width="12.5%"><a href="https://github.com/gerases"><img src="https://avatars.githubusercontent.com/u/8953623?v=4?s=100" width="100px;" alt="gerases"/><br /><sub><b>gerases</b></sub></a><br /><a href="#content-gerases" title="Content">🖋</a></td> <td align="center" valign="top" width="12.5%"><a href="https://github.com/gerases"><img src="https://avatars.githubusercontent.com/u/8953623?v=4?s=100" width="100px;" alt="gerases"/><br /><sub><b>gerases</b></sub></a><br /><a href="#content-gerases" title="Content">🖋</a></td>
<td align="center" valign="top" width="12.5%"><a href="https://github.com/AnonimAnonim2245"><img src="https://avatars.githubusercontent.com/u/98339220?v=4?s=100" width="100px;" alt="Luca Plian"/><br /><sub><b>Luca Plian</b></sub></a><br /><a href="https://github.com/rust-lang/rustlings/commits?author=AnonimAnonim2245" title="Code">💻</a></td>
<td align="center" valign="top" width="12.5%"><a href="https://reifenrath.dev/"><img src="https://avatars.githubusercontent.com/u/18126097?v=4?s=100" width="100px;" alt="René Reifenrath"/><br /><sub><b>René Reifenrath</b></sub></a><br /><a href="#content-reifenrath-dev" title="Content">🖋</a></td>
<td align="center" valign="top" width="12.5%"><a href="https://github.com/peterneave"><img src="https://avatars.githubusercontent.com/u/7982708?v=4?s=100" width="100px;" alt="Peter Neave"/><br /><sub><b>Peter Neave</b></sub></a><br /><a href="#infra-peterneave" title="Infrastructure (Hosting, Build-Tools, etc)">🚇</a></td>
<td align="center" valign="top" width="12.5%"><a href="http://www.janb1.com"><img src="https://avatars.githubusercontent.com/u/5552248?v=4?s=100" width="100px;" alt="Jan"/><br /><sub><b>Jan</b></sub></a><br /><a href="#content-JanB1" title="Content">🖋</a></td>
<td align="center" valign="top" width="12.5%"><a href="http://www.kylev.com/"><img src="https://avatars.githubusercontent.com/u/46888?v=4?s=100" width="100px;" alt="Kyle VanderBeek"/><br /><sub><b>Kyle VanderBeek</b></sub></a><br /><a href="#infra-kylev" title="Infrastructure (Hosting, Build-Tools, etc)">🚇</a></td>
<td align="center" valign="top" width="12.5%"><a href="https://github.com/pavedroad"><img src="https://avatars.githubusercontent.com/u/138004431?v=4?s=100" width="100px;" alt="pavedroad"/><br /><sub><b>pavedroad</b></sub></a><br /><a href="#content-pavedroad" title="Content">🖋</a></td>
<td align="center" valign="top" width="12.5%"><a href="https://github.com/hyphena"><img src="https://avatars.githubusercontent.com/u/26529488?v=4?s=100" width="100px;" alt="luna"/><br /><sub><b>luna</b></sub></a><br /><a href="#content-hyphena" title="Content">🖋</a></td>
</tr>
<tr>
<td align="center" valign="top" width="12.5%"><a href="https://github.com/evanmiller2112"><img src="https://avatars.githubusercontent.com/u/28488957?v=4?s=100" width="100px;" alt="Evan Miller"/><br /><sub><b>Evan Miller</b></sub></a><br /><a href="#content-evanmiller2112" title="Content">🖋</a></td>
<td align="center" valign="top" width="12.5%"><a href="https://github.com/luvchurchill"><img src="https://avatars.githubusercontent.com/u/46406654?v=4?s=100" width="100px;" alt="luvchurchill"/><br /><sub><b>luvchurchill</b></sub></a><br /><a href="https://github.com/rust-lang/rustlings/commits?author=luvchurchill" title="Code">💻</a></td>
<td align="center" valign="top" width="12.5%"><a href="https://leverimmy.top/"><img src="https://avatars.githubusercontent.com/u/47663913?v=4?s=100" width="100px;" alt="Ze-en Xiong"/><br /><sub><b>Ze-en Xiong</b></sub></a><br /><a href="#content-LeverImmy" title="Content">🖋</a></td>
<td align="center" valign="top" width="12.5%"><a href="https://github.com/parnavh"><img src="https://avatars.githubusercontent.com/u/45985534?v=4?s=100" width="100px;" alt="Parnav Harinathan"/><br /><sub><b>Parnav Harinathan</b></sub></a><br /><a href="#content-parnavh" title="Content">🖋</a></td>
<td align="center" valign="top" width="12.5%"><a href="https://github.com/0Ahmed-0"><img src="https://avatars.githubusercontent.com/u/111569638?v=4?s=100" width="100px;" alt="0Ahmed-0"/><br /><sub><b>0Ahmed-0</b></sub></a><br /><a href="#content-0Ahmed-0" title="Content">🖋</a></td>
<td align="center" valign="top" width="12.5%"><a href="https://github.com/guizo792"><img src="https://avatars.githubusercontent.com/u/95940388?v=4?s=100" width="100px;" alt="guizo792"/><br /><sub><b>guizo792</b></sub></a><br /><a href="#content-guizo792" title="Content">🖋</a></td>
<td align="center" valign="top" width="12.5%"><a href="https://github.com/kazu728"><img src="https://avatars.githubusercontent.com/u/34614358?v=4?s=100" width="100px;" alt="Kazuki Matsuo"/><br /><sub><b>Kazuki Matsuo</b></sub></a><br /><a href="https://github.com/rust-lang/rustlings/commits?author=kazu728" title="Code">💻</a></td>
</tr> </tr>
</tbody> </tbody>
</table> </table>

View file

@ -167,7 +167,7 @@
- **structs3**: Clarifed the hint - **structs3**: Clarifed the hint
- **quiz2, as_ref_mut, options1, traits1, traits2**: Clarified hints - **quiz2, as_ref_mut, options1, traits1, traits2**: Clarified hints
- **traits1, traits2, cli**: Tidied up unmatching backticks - **traits1, traits2, cli**: Tidied up unmatching backticks
- **enums2**: Removed unneccessary indirection of self - **enums2**: Removed unnecessary indirection of self
- **enums3**: Added an extra tuple comment - **enums3**: Added an extra tuple comment
#### Housekeeping #### Housekeeping

417
Cargo.lock generated
View file

@ -4,18 +4,18 @@ version = 3
[[package]] [[package]]
name = "aho-corasick" name = "aho-corasick"
version = "0.7.20" version = "1.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cc936419f96fa211c1b9166887b38e5e40b19958e5b895be7c1f93adec7071ac" checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916"
dependencies = [ dependencies = [
"memchr", "memchr",
] ]
[[package]] [[package]]
name = "anstream" name = "anstream"
version = "0.5.0" version = "0.6.13"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b1f58811cfac344940f1a400b6e6231ce35171f614f26439e80f8c1465c5cc0c" checksum = "d96bd03f33fe50a863e394ee9718a706f988b9079b20c3784fb726e7678b62fb"
dependencies = [ dependencies = [
"anstyle", "anstyle",
"anstyle-parse", "anstyle-parse",
@ -27,43 +27,49 @@ dependencies = [
[[package]] [[package]]
name = "anstyle" name = "anstyle"
version = "1.0.3" version = "1.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b84bf0a05bbb2a83e5eb6fa36bb6e87baa08193c35ff52bbf6b38d8af2890e46" checksum = "8901269c6307e8d93993578286ac0edf7f195079ffff5ebdeea6a59ffb7e36bc"
[[package]] [[package]]
name = "anstyle-parse" name = "anstyle-parse"
version = "0.2.1" version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "938874ff5980b03a87c5524b3ae5b59cf99b1d6bc836848df7bc5ada9643c333" checksum = "c75ac65da39e5fe5ab759307499ddad880d724eed2f6ce5b5e8a26f4f387928c"
dependencies = [ dependencies = [
"utf8parse", "utf8parse",
] ]
[[package]] [[package]]
name = "anstyle-query" name = "anstyle-query"
version = "1.0.0" version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b" checksum = "e28923312444cdd728e4738b3f9c9cac739500909bb3d3c94b43551b16517648"
dependencies = [ dependencies = [
"windows-sys 0.48.0", "windows-sys 0.52.0",
] ]
[[package]] [[package]]
name = "anstyle-wincon" name = "anstyle-wincon"
version = "2.1.0" version = "3.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "58f54d10c6dfa51283a066ceab3ec1ab78d13fae00aa49243a45e4571fb79dfd" checksum = "1cd54b81ec8d6180e24654d0b371ad22fc3dd083b6ff8ba325b72e00c87660a7"
dependencies = [ dependencies = [
"anstyle", "anstyle",
"windows-sys 0.48.0", "windows-sys 0.52.0",
] ]
[[package]] [[package]]
name = "assert_cmd" name = "anyhow"
version = "2.0.12" version = "1.0.81"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "88903cb14723e4d4003335bb7f8a14f27691649105346a0f0957466c096adfe6" checksum = "0952808a6c2afd1aa8947271f3a60f1a6763c7b912d210184c5149b5cf147247"
[[package]]
name = "assert_cmd"
version = "2.0.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed72493ac66d5804837f480ab3766c72bdfab91a65e565fc54fa9e42db0073a8"
dependencies = [ dependencies = [
"anstyle", "anstyle",
"bstr", "bstr",
@ -76,9 +82,9 @@ dependencies = [
[[package]] [[package]]
name = "autocfg" name = "autocfg"
version = "1.1.0" version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" checksum = "f1fdabc7756949593fe60f30ec81974b613357de856987752631dea1e3394c80"
[[package]] [[package]]
name = "bitflags" name = "bitflags"
@ -88,15 +94,15 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]] [[package]]
name = "bitflags" name = "bitflags"
version = "2.4.1" version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07" checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1"
[[package]] [[package]]
name = "bstr" name = "bstr"
version = "1.6.2" version = "1.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4c2f7349907b712260e64b0afe2f84692af14a454be26187d9df565c7f69266a" checksum = "05efc5cfd9110c8416e471df0e96702d58690178e206e61b7173706673c93706"
dependencies = [ dependencies = [
"memchr", "memchr",
"regex-automata", "regex-automata",
@ -111,9 +117,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]] [[package]]
name = "clap" name = "clap"
version = "4.4.3" version = "4.5.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "84ed82781cea27b43c9b106a979fe450a13a31aab0500595fb3fc06616de08e6" checksum = "90bc066a67923782aa8515dbaea16946c5bcc5addbd668bb80af688e53e548a0"
dependencies = [ dependencies = [
"clap_builder", "clap_builder",
"clap_derive", "clap_derive",
@ -121,9 +127,9 @@ dependencies = [
[[package]] [[package]]
name = "clap_builder" name = "clap_builder"
version = "4.4.2" version = "4.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2bb9faaa7c2ef94b2743a21f5a29e6f0010dff4caa69ac8e9d6cf8b6fa74da08" checksum = "ae129e2e766ae0ec03484e609954119f123cc1fe650337e155d03b022f24f7b4"
dependencies = [ dependencies = [
"anstream", "anstream",
"anstyle", "anstyle",
@ -133,9 +139,9 @@ dependencies = [
[[package]] [[package]]
name = "clap_derive" name = "clap_derive"
version = "4.4.2" version = "4.5.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0862016ff20d69b84ef8247369fabf5c008a7417002411897d40ee1f4532b873" checksum = "528131438037fd55894f62d6e9f068b8f45ac57ffa77517819645d10aed04f64"
dependencies = [ dependencies = [
"heck", "heck",
"proc-macro2", "proc-macro2",
@ -145,9 +151,9 @@ dependencies = [
[[package]] [[package]]
name = "clap_lex" name = "clap_lex"
version = "0.5.1" version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cd7cc57abe963c6d3b9d8be5b06ba7c8957a930305ca90304f24ef040aa6f961" checksum = "98cc8fbded0c607b7ba9dd60cd98df59af97e84d24e49c8557331cfc26d301ce"
[[package]] [[package]]
name = "colorchoice" name = "colorchoice"
@ -157,35 +163,31 @@ checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7"
[[package]] [[package]]
name = "console" name = "console"
version = "0.15.5" version = "0.15.8"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c3d79fbe8970a77e3e34151cc13d3b3e248aa0faaecb9f6091fa07ebefe5ad60" checksum = "0e1f83fc076bd6dd27517eacdf25fef6c4dfe5f1d7448bafaaf3a26f13b5e4eb"
dependencies = [ dependencies = [
"encode_unicode", "encode_unicode",
"lazy_static", "lazy_static",
"libc", "libc",
"unicode-width", "unicode-width",
"windows-sys 0.42.0", "windows-sys 0.52.0",
] ]
[[package]] [[package]]
name = "crossbeam-channel" name = "crossbeam-channel"
version = "0.5.8" version = "0.5.12"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a33c2bf77f2df06183c3aa30d1e96c0695a313d4f9c453cc3762a6db39f99200" checksum = "ab3db02a9c5b5121e1e42fbdb1aeb65f5e02624cc58c43f2884c6ccac0b82f95"
dependencies = [ dependencies = [
"cfg-if",
"crossbeam-utils", "crossbeam-utils",
] ]
[[package]] [[package]]
name = "crossbeam-utils" name = "crossbeam-utils"
version = "0.8.16" version = "0.8.19"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a22b2d63d4d1dc0b7f1b6b2747dd0088008a9be28b6ddf0b1e7d335e3037294" checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345"
dependencies = [
"cfg-if",
]
[[package]] [[package]]
name = "difflib" name = "difflib"
@ -201,9 +203,9 @@ checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10"
[[package]] [[package]]
name = "either" name = "either"
version = "1.9.0" version = "1.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" checksum = "11157ac094ffbdde99aa67b23417ebdd801842852b500e395a45a9c0aac03e4a"
[[package]] [[package]]
name = "encode_unicode" name = "encode_unicode"
@ -218,15 +220,25 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
[[package]] [[package]]
name = "filetime" name = "errno"
version = "0.2.22" version = "0.3.8"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d4029edd3e734da6fe05b6cd7bd2960760a616bd2ddd0d59a0124746d6272af0" checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245"
dependencies = [
"libc",
"windows-sys 0.52.0",
]
[[package]]
name = "filetime"
version = "0.2.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1ee447700ac8aa0b2f2bd7bc4462ad686ba06baa6727ac149a2d6277f0d240fd"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
"libc", "libc",
"redox_syscall", "redox_syscall",
"windows-sys 0.48.0", "windows-sys 0.52.0",
] ]
[[package]] [[package]]
@ -255,30 +267,30 @@ checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b"
[[package]] [[package]]
name = "hashbrown" name = "hashbrown"
version = "0.14.0" version = "0.14.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2c6201b9ff9fd90a5a3bac2e56a830d0caa509576f0e503818ee82c181b3437a" checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604"
[[package]] [[package]]
name = "heck" name = "heck"
version = "0.4.1" version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
[[package]] [[package]]
name = "home" name = "home"
version = "0.5.4" version = "0.5.9"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "747309b4b440c06d57b0b25f2aee03ee9b5e5397d288c60e21fc709bb98a7408" checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5"
dependencies = [ dependencies = [
"winapi", "windows-sys 0.52.0",
] ]
[[package]] [[package]]
name = "indexmap" name = "indexmap"
version = "2.0.0" version = "2.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d5477fe2230a79769d8dc68e0eabf5437907c0457a5614a9e8dddb67f65eb65d" checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26"
dependencies = [ dependencies = [
"equivalent", "equivalent",
"hashbrown", "hashbrown",
@ -286,9 +298,9 @@ dependencies = [
[[package]] [[package]]
name = "indicatif" name = "indicatif"
version = "0.17.6" version = "0.17.8"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b297dc40733f23a0e52728a58fa9489a5b7638a324932de16b41adc3ef80730" checksum = "763a5a8f45087d6bcea4222e7b72c291a054edf80e4ef6efd2a4979878c7bea3"
dependencies = [ dependencies = [
"console", "console",
"instant", "instant",
@ -326,20 +338,11 @@ dependencies = [
"cfg-if", "cfg-if",
] ]
[[package]]
name = "itertools"
version = "0.10.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473"
dependencies = [
"either",
]
[[package]] [[package]]
name = "itoa" name = "itoa"
version = "1.0.6" version = "1.0.11"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6" checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b"
[[package]] [[package]]
name = "kqueue" name = "kqueue"
@ -369,30 +372,33 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
[[package]] [[package]]
name = "libc" name = "libc"
version = "0.2.150" version = "0.2.153"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "89d92a4743f9a61002fae18374ed11e7973f530cb3a3255fb354818118b2203c" checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd"
[[package]]
name = "linux-raw-sys"
version = "0.4.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c"
[[package]] [[package]]
name = "log" name = "log"
version = "0.4.17" version = "0.4.21"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c"
dependencies = [
"cfg-if",
]
[[package]] [[package]]
name = "memchr" name = "memchr"
version = "2.6.3" version = "2.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f232d6ef707e1956a43342693d2a31e72989554d58299d7a88738cc95b0d35c" checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149"
[[package]] [[package]]
name = "mio" name = "mio"
version = "0.8.9" version = "0.8.11"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3dce281c5e46beae905d4de1870d8b1509a9142b62eedf18b443b011ca8343d0" checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c"
dependencies = [ dependencies = [
"libc", "libc",
"log", "log",
@ -412,7 +418,7 @@ version = "6.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6205bd8bb1e454ad2e27422015fb5e4f2bcc7e08fa8f27058670d208324a4d2d" checksum = "6205bd8bb1e454ad2e27422015fb5e4f2bcc7e08fa8f27058670d208324a4d2d"
dependencies = [ dependencies = [
"bitflags 2.4.1", "bitflags 2.5.0",
"crossbeam-channel", "crossbeam-channel",
"filetime", "filetime",
"fsevent-sys", "fsevent-sys",
@ -438,9 +444,9 @@ dependencies = [
[[package]] [[package]]
name = "num-traits" name = "num-traits"
version = "0.2.15" version = "0.2.18"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" checksum = "da0df0e5185db44f69b44f26786fe401b6c293d1907744beaa7fa62b2e5a517a"
dependencies = [ dependencies = [
"autocfg", "autocfg",
] ]
@ -453,20 +459,19 @@ checksum = "830b246a0e5f20af87141b25c173cd1b609bd7779a4617d6ec582abaf90870f3"
[[package]] [[package]]
name = "portable-atomic" name = "portable-atomic"
version = "1.4.3" version = "1.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "31114a898e107c51bb1609ffaf55a0e011cf6a4d7f1170d0015a165082c0338b" checksum = "7170ef9988bc169ba16dd36a7fa041e5c4cbeb6a35b76d4c03daded371eae7c0"
[[package]] [[package]]
name = "predicates" name = "predicates"
version = "3.0.3" version = "3.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09963355b9f467184c04017ced4a2ba2d75cbcb4e7462690d388233253d4b1a9" checksum = "68b87bfd4605926cdfefc1c3b5f8fe560e3feca9d5552cf68c466d3d8236c7e8"
dependencies = [ dependencies = [
"anstyle", "anstyle",
"difflib", "difflib",
"float-cmp", "float-cmp",
"itertools",
"normalize-line-endings", "normalize-line-endings",
"predicates-core", "predicates-core",
"regex", "regex",
@ -490,36 +495,48 @@ dependencies = [
[[package]] [[package]]
name = "proc-macro2" name = "proc-macro2"
version = "1.0.53" version = "1.0.79"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ba466839c78239c09faf015484e5cc04860f88242cff4d03eb038f04b4699b73" checksum = "e835ff2298f5721608eb1a980ecaee1aef2c132bf95ecc026a11b7bf3c01c02e"
dependencies = [ dependencies = [
"unicode-ident", "unicode-ident",
] ]
[[package]] [[package]]
name = "quote" name = "quote"
version = "1.0.26" version = "1.0.35"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4424af4bf778aae2051a77b60283332f386554255d722233d09fbfc7e30da2fc" checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
] ]
[[package]] [[package]]
name = "redox_syscall" name = "redox_syscall"
version = "0.3.5" version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa"
dependencies = [ dependencies = [
"bitflags 1.3.2", "bitflags 1.3.2",
] ]
[[package]] [[package]]
name = "regex" name = "regex"
version = "1.7.2" version = "1.10.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cce168fea28d3e05f158bda4576cf0c844d5045bc2cc3620fa0292ed5bb5814c" checksum = "c117dbdfde9c8308975b6a18d71f3f385c89461f7b3fb054288ecf2a2058ba4c"
dependencies = [
"aho-corasick",
"memchr",
"regex-automata",
"regex-syntax",
]
[[package]]
name = "regex-automata"
version = "0.4.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea"
dependencies = [ dependencies = [
"aho-corasick", "aho-corasick",
"memchr", "memchr",
@ -527,40 +544,49 @@ dependencies = [
] ]
[[package]] [[package]]
name = "regex-automata" name = "regex-syntax"
version = "0.3.8" version = "0.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c2f401f4955220693b56f8ec66ee9c78abffd8d1c4f23dc41a23839eb88f0795" checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56"
[[package]] [[package]]
name = "regex-syntax" name = "rustix"
version = "0.6.29" version = "0.38.32"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" checksum = "65e04861e65f21776e67888bfbea442b3642beaa0138fdb1dd7a84a52dffdb89"
dependencies = [
"bitflags 2.5.0",
"errno",
"libc",
"linux-raw-sys",
"windows-sys 0.52.0",
]
[[package]] [[package]]
name = "rustlings" name = "rustlings"
version = "5.6.1" version = "5.6.1"
dependencies = [ dependencies = [
"anyhow",
"assert_cmd", "assert_cmd",
"clap", "clap",
"console", "console",
"glob", "glob",
"home",
"indicatif", "indicatif",
"notify-debouncer-mini", "notify-debouncer-mini",
"predicates", "predicates",
"regex",
"serde", "serde",
"serde_json", "serde_json",
"toml", "shlex",
"toml_edit",
"which",
"winnow",
] ]
[[package]] [[package]]
name = "ryu" name = "ryu"
version = "1.0.13" version = "1.0.17"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041" checksum = "e86697c916019a8588c99b5fac3cead74ec0b4b819707a682fd4d23fa0ce1ba1"
[[package]] [[package]]
name = "same-file" name = "same-file"
@ -573,18 +599,18 @@ dependencies = [
[[package]] [[package]]
name = "serde" name = "serde"
version = "1.0.158" version = "1.0.197"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "771d4d9c4163ee138805e12c710dd365e4f44be8be0503cb1bb9eb989425d9c9" checksum = "3fb1c873e1b9b056a4dc4c0c198b24c3ffa059243875552b2bd0933b1aee4ce2"
dependencies = [ dependencies = [
"serde_derive", "serde_derive",
] ]
[[package]] [[package]]
name = "serde_derive" name = "serde_derive"
version = "1.0.158" version = "1.0.197"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e801c1712f48475582b7696ac71e0ca34ebb30e09338425384269d9717c62cad" checksum = "7eb0b34b42edc17f6b7cac84a52a1c5f0e1bb2227e997ca9011ea3dd34e8610b"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@ -593,9 +619,9 @@ dependencies = [
[[package]] [[package]]
name = "serde_json" name = "serde_json"
version = "1.0.94" version = "1.0.115"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1c533a59c9d8a93a09c6ab31f0fd5e5f4dd1b8fc9434804029839884765d04ea" checksum = "12dc5c46daa8e9fdf4f5e71b6cf9a53f2487da0e86e55808e2d35539666497dd"
dependencies = [ dependencies = [
"itoa", "itoa",
"ryu", "ryu",
@ -604,24 +630,30 @@ dependencies = [
[[package]] [[package]]
name = "serde_spanned" name = "serde_spanned"
version = "0.6.3" version = "0.6.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "96426c9936fd7a0124915f9185ea1d20aa9445cc9821142f0a73bc9207a2e186" checksum = "eb3622f419d1296904700073ea6cc23ad690adbd66f13ea683df73298736f0c1"
dependencies = [ dependencies = [
"serde", "serde",
] ]
[[package]] [[package]]
name = "strsim" name = "shlex"
version = "0.10.0" version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
[[package]]
name = "strsim"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5ee073c9e4cd00e28217186dbe12796d692868f432bf2e97ee73bed0c56dfa01"
[[package]] [[package]]
name = "syn" name = "syn"
version = "2.0.8" version = "2.0.55"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bcc02725fd69ab9f26eab07fad303e2497fad6fb9eba4f96c4d1687bdf704ad9" checksum = "002a1b3dbf967edfafc32655d0f377ab0bb7b994aa1d32c8cc7e9b8bf3ebb8f0"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@ -634,32 +666,20 @@ version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3369f5ac52d5eb6ab48c6b4ffdc8efbcad6b89c765749064ba298f2c68a16a76" checksum = "3369f5ac52d5eb6ab48c6b4ffdc8efbcad6b89c765749064ba298f2c68a16a76"
[[package]]
name = "toml"
version = "0.7.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dd79e69d3b627db300ff956027cc6c3798cef26d22526befdfcd12feeb6d2257"
dependencies = [
"serde",
"serde_spanned",
"toml_datetime",
"toml_edit",
]
[[package]] [[package]]
name = "toml_datetime" name = "toml_datetime"
version = "0.6.3" version = "0.6.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7cda73e2f1397b1262d6dfdcef8aafae14d1de7748d66822d3bfeeb6d03e5e4b" checksum = "3550f4e9685620ac18a50ed434eb3aec30db8ba93b0287467bca5826ea25baf1"
dependencies = [ dependencies = [
"serde", "serde",
] ]
[[package]] [[package]]
name = "toml_edit" name = "toml_edit"
version = "0.19.15" version = "0.22.9"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" checksum = "8e40bb779c5187258fd7aad0eb68cb8706a0a81fa712fbea808ab43c4b8374c4"
dependencies = [ dependencies = [
"indexmap", "indexmap",
"serde", "serde",
@ -670,15 +690,15 @@ dependencies = [
[[package]] [[package]]
name = "unicode-ident" name = "unicode-ident"
version = "1.0.8" version = "1.0.12"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4" checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
[[package]] [[package]]
name = "unicode-width" name = "unicode-width"
version = "0.1.10" version = "0.1.11"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b" checksum = "e51733f11c9c4f72aa0c160008246859e340b00807569a0da0e7a1079b27ba85"
[[package]] [[package]]
name = "utf8parse" name = "utf8parse"
@ -697,9 +717,9 @@ dependencies = [
[[package]] [[package]]
name = "walkdir" name = "walkdir"
version = "2.3.3" version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "36df944cda56c7d8d8b7496af378e6b16de9284591917d307c9b4d313c44e698" checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b"
dependencies = [ dependencies = [
"same-file", "same-file",
"winapi-util", "winapi-util",
@ -711,6 +731,18 @@ version = "0.11.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
[[package]]
name = "which"
version = "6.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8211e4f58a2b2805adfbefbc07bab82958fc91e3836339b1ab7ae32465dce0d7"
dependencies = [
"either",
"home",
"rustix",
"winsafe",
]
[[package]] [[package]]
name = "winapi" name = "winapi"
version = "0.3.9" version = "0.3.9"
@ -729,9 +761,9 @@ checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
[[package]] [[package]]
name = "winapi-util" name = "winapi-util"
version = "0.1.5" version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596"
dependencies = [ dependencies = [
"winapi", "winapi",
] ]
@ -742,28 +774,22 @@ version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "windows-sys"
version = "0.42.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7"
dependencies = [
"windows_aarch64_gnullvm 0.42.2",
"windows_aarch64_msvc 0.42.2",
"windows_i686_gnu 0.42.2",
"windows_i686_msvc 0.42.2",
"windows_x86_64_gnu 0.42.2",
"windows_x86_64_gnullvm 0.42.2",
"windows_x86_64_msvc 0.42.2",
]
[[package]] [[package]]
name = "windows-sys" name = "windows-sys"
version = "0.48.0" version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
dependencies = [ dependencies = [
"windows-targets", "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.4",
] ]
[[package]] [[package]]
@ -782,10 +808,19 @@ dependencies = [
] ]
[[package]] [[package]]
name = "windows_aarch64_gnullvm" name = "windows-targets"
version = "0.42.2" version = "0.52.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" checksum = "7dd37b7e5ab9018759f893a1952c9420d060016fc19a472b4bb20d1bdd694d1b"
dependencies = [
"windows_aarch64_gnullvm 0.52.4",
"windows_aarch64_msvc 0.52.4",
"windows_i686_gnu 0.52.4",
"windows_i686_msvc 0.52.4",
"windows_x86_64_gnu 0.52.4",
"windows_x86_64_gnullvm 0.52.4",
"windows_x86_64_msvc 0.52.4",
]
[[package]] [[package]]
name = "windows_aarch64_gnullvm" name = "windows_aarch64_gnullvm"
@ -794,10 +829,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8"
[[package]] [[package]]
name = "windows_aarch64_msvc" name = "windows_aarch64_gnullvm"
version = "0.42.2" version = "0.52.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" checksum = "bcf46cf4c365c6f2d1cc93ce535f2c8b244591df96ceee75d8e83deb70a9cac9"
[[package]] [[package]]
name = "windows_aarch64_msvc" name = "windows_aarch64_msvc"
@ -806,10 +841,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"
[[package]] [[package]]
name = "windows_i686_gnu" name = "windows_aarch64_msvc"
version = "0.42.2" version = "0.52.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" checksum = "da9f259dd3bcf6990b55bffd094c4f7235817ba4ceebde8e6d11cd0c5633b675"
[[package]] [[package]]
name = "windows_i686_gnu" name = "windows_i686_gnu"
@ -818,10 +853,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e"
[[package]] [[package]]
name = "windows_i686_msvc" name = "windows_i686_gnu"
version = "0.42.2" version = "0.52.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" checksum = "b474d8268f99e0995f25b9f095bc7434632601028cf86590aea5c8a5cb7801d3"
[[package]] [[package]]
name = "windows_i686_msvc" name = "windows_i686_msvc"
@ -830,10 +865,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406"
[[package]] [[package]]
name = "windows_x86_64_gnu" name = "windows_i686_msvc"
version = "0.42.2" version = "0.52.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" checksum = "1515e9a29e5bed743cb4415a9ecf5dfca648ce85ee42e15873c3cd8610ff8e02"
[[package]] [[package]]
name = "windows_x86_64_gnu" name = "windows_x86_64_gnu"
@ -842,10 +877,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e"
[[package]] [[package]]
name = "windows_x86_64_gnullvm" name = "windows_x86_64_gnu"
version = "0.42.2" version = "0.52.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" checksum = "5eee091590e89cc02ad514ffe3ead9eb6b660aedca2183455434b93546371a03"
[[package]] [[package]]
name = "windows_x86_64_gnullvm" name = "windows_x86_64_gnullvm"
@ -854,10 +889,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc"
[[package]] [[package]]
name = "windows_x86_64_msvc" name = "windows_x86_64_gnullvm"
version = "0.42.2" version = "0.52.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" checksum = "77ca79f2451b49fa9e2af39f0747fe999fcda4f5e241b2898624dca97a1f2177"
[[package]] [[package]]
name = "windows_x86_64_msvc" name = "windows_x86_64_msvc"
@ -866,10 +901,22 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"
[[package]] [[package]]
name = "winnow" name = "windows_x86_64_msvc"
version = "0.5.15" version = "0.52.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7c2e3184b9c4e92ad5167ca73039d0c42476302ab603e2fec4487511f38ccefc" checksum = "32b752e52a2da0ddfbdbcc6fceadfeede4c939ed16d13e648833a61dfb611ed8"
[[package]]
name = "winnow"
version = "0.6.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dffa400e67ed5a4dd237983829e66475f0a4a26938c4b04c21baede6262215b8"
dependencies = [ dependencies = [
"memchr", "memchr",
] ]
[[package]]
name = "winsafe"
version = "0.0.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d135d17ab770252ad95e9a872d365cf3090e3be864a34ab46f48555993efc904"

View file

@ -9,22 +9,23 @@ authors = [
edition = "2021" edition = "2021"
[dependencies] [dependencies]
indicatif = "0.17.6" anyhow = "1.0.81"
console = "0.15" clap = { version = "4.5.4", features = ["derive"] }
console = "0.15.8"
indicatif = "0.17.8"
notify-debouncer-mini = "0.4.1" notify-debouncer-mini = "0.4.1"
toml = "0.7.6" serde_json = "1.0.115"
regex = "1.5" serde = { version = "1.0.197", features = ["derive"] }
serde = { version = "1.0", features = ["derive"] } shlex = "1.3.0"
serde_json = "1.0.81" toml_edit = { version = "0.22.9", default-features = false, features = ["parse", "serde"] }
home = "0.5.3" which = "6.0.1"
glob = "0.3.0" winnow = "0.6.5"
clap = { version = "4.4.0", features = ["derive"] }
[[bin]] [[bin]]
name = "rustlings" name = "rustlings"
path = "src/main.rs" path = "src/main.rs"
[dev-dependencies] [dev-dependencies]
assert_cmd = "2.0.12" assert_cmd = "2.0.14"
predicates = "3.0.3"
glob = "0.3.0" glob = "0.3.0"
predicates = "3.1.0"

View file

@ -6,6 +6,7 @@
pub fn bigger(a: i32, b: i32) -> i32 { pub fn bigger(a: i32, b: i32) -> i32 {
// Complete this function to return the bigger number! // Complete this function to return the bigger number!
// If both numbers are equal, any of them can be returned.
// Do not use: // Do not use:
// - another function call // - another function call
// - additional variables // - additional variables

View file

@ -11,7 +11,7 @@
fn main() { fn main() {
let vec0 = vec![22, 44, 66]; let vec0 = vec![22, 44, 66];
let mut vec1 = fill_vec(vec0); let vec1 = fill_vec(vec0);
assert_eq!(vec0, vec![22, 44, 66]); assert_eq!(vec0, vec![22, 44, 66]);
assert_eq!(vec1, vec![22, 44, 66, 88]); assert_eq!(vec1, vec![22, 44, 66, 88]);

View file

@ -12,7 +12,7 @@
fn main() { fn main() {
let vec0 = vec![22, 44, 66]; let vec0 = vec![22, 44, 66];
let mut vec1 = fill_vec(vec0); let vec1 = fill_vec(vec0);
assert_eq!(vec1, vec![22, 44, 66, 88]); assert_eq!(vec1, vec![22, 44, 66, 88]);
} }

View file

@ -13,7 +13,7 @@
fn main() { fn main() {
let vec0 = vec![22, 44, 66]; let vec0 = vec![22, 44, 66];
let mut vec1 = fill_vec(vec0); let vec1 = fill_vec(vec0);
assert_eq!(vec1, vec![22, 44, 66, 88]); assert_eq!(vec1, vec![22, 44, 66, 88]);
} }

View file

@ -11,7 +11,7 @@ fn trim_me(input: &str) -> String {
} }
fn compose_me(input: &str) -> String { fn compose_me(input: &str) -> String {
// TODO: Add " world!" to the string! There's multiple ways to do this! // TODO: Add " world!" to the string! There are multiple ways to do this!
??? ???
} }

View file

@ -4,10 +4,11 @@
// the form : "<team_1_name>,<team_2_name>,<team_1_goals>,<team_2_goals>" // the form : "<team_1_name>,<team_2_name>,<team_1_goals>,<team_2_goals>"
// Example: England,France,4,2 (England scored 4 goals, France 2). // Example: England,France,4,2 (England scored 4 goals, France 2).
// //
// You have to build a scores table containing the name of the team, goals the // You have to build a scores table containing the name of the team, the total
// team scored, and goals the team conceded. One approach to build the scores // number of goals the team scored, and the total number of goals the team
// table is to use a Hashmap. The solution is partially written to use a // conceded. One approach to build the scores table is to use a Hashmap.
// Hashmap, complete it to pass the test. // The solution is partially written to use a Hashmap,
// complete it to pass the test.
// //
// Make me pass the tests! // Make me pass the tests!
// //

View file

@ -8,7 +8,7 @@
pub fn factorial(num: u64) -> u64 { pub fn factorial(num: u64) -> u64 {
// Complete this function to return the factorial of num // Complete this function to return the factorial of num
// Do not use: // Do not use:
// - return // - early returns (using the `return` keyword explicitly)
// Try not to use: // Try not to use:
// - imperative style loops (for, while) // - imperative style loops (for, while)
// - additional variables // - additional variables

View file

@ -27,22 +27,18 @@ impl Queue {
} }
fn send_tx(q: Queue, tx: mpsc::Sender<u32>) -> () { fn send_tx(q: Queue, tx: mpsc::Sender<u32>) -> () {
let qc = Arc::new(q);
let qc1 = Arc::clone(&qc);
let qc2 = Arc::clone(&qc);
thread::spawn(move || { thread::spawn(move || {
for val in &qc1.first_half { for val in q.first_half {
println!("sending {:?}", val); println!("sending {:?}", val);
tx.send(*val).unwrap(); tx.send(val).unwrap();
thread::sleep(Duration::from_secs(1)); thread::sleep(Duration::from_secs(1));
} }
}); });
thread::spawn(move || { thread::spawn(move || {
for val in &qc2.second_half { for val in q.second_half {
println!("sending {:?}", val); println!("sending {:?}", val);
tx.send(*val).unwrap(); tx.send(val).unwrap();
thread::sleep(Duration::from_secs(1)); thread::sleep(Duration::from_secs(1));
} }
}); });

View file

@ -24,8 +24,9 @@ impl Default for Person {
} }
} }
// Your task is to complete this implementation in order for the line `let p =
// Person::from("Mark,20")` to compile Please note that you'll need to parse the // Your task is to complete this implementation in order for the line `let p1 =
// Person::from("Mark,20")` to compile. Please note that you'll need to parse the
// age component into a `usize` with something like `"4".parse::<usize>()`. The // age component into a `usize` with something like `"4".parse::<usize>()`. The
// outcome of this needs to be handled appropriately. // outcome of this needs to be handled appropriately.
// //
@ -43,8 +44,7 @@ impl Default for Person {
// I AM NOT DONE // I AM NOT DONE
impl From<&str> for Person { impl From<&str> for Person {
fn from(s: &str) -> Person { fn from(s: &str) -> Person {}
}
} }
fn main() { fn main() {
@ -127,14 +127,14 @@ mod tests {
#[test] #[test]
fn test_trailing_comma() { fn test_trailing_comma() {
let p: Person = Person::from("Mike,32,"); let p: Person = Person::from("Mike,32,");
assert_eq!(p.name, "Mike"); assert_eq!(p.name, "John");
assert_eq!(p.age, 32); assert_eq!(p.age, 30);
} }
#[test] #[test]
fn test_trailing_comma_and_some_string() { fn test_trailing_comma_and_some_string() {
let p: Person = Person::from("Mike,32,man"); let p: Person = Person::from("Mike,32,dog");
assert_eq!(p.name, "Mike"); assert_eq!(p.name, "John");
assert_eq!(p.age, 32); assert_eq!(p.age, 30);
} }
} }

View file

@ -17,11 +17,11 @@
| error_handling | §9 | | error_handling | §9 |
| generics | §10 | | generics | §10 |
| traits | §10.2 | | traits | §10.2 |
| tests | §11.1 |
| lifetimes | §10.3 | | lifetimes | §10.3 |
| tests | §11.1 |
| iterators | §13.2-4 | | iterators | §13.2-4 |
| threads | §16.1-3 |
| smart_pointers | §15, §16.3 | | smart_pointers | §15, §16.3 |
| threads | §16.1-3 |
| macros | §19.5 | | macros | §19.5 |
| clippy | §21.4 | | clippy | §21.4 |
| conversions | n/a | | conversions | n/a |

View file

@ -145,7 +145,7 @@ after the `->`. This is where the function's return type should be -- take a
look at the `is_even` function for an example! look at the `is_even` function for an example!
Also: Did you figure out that, technically, `u32` would be the more fitting type Also: Did you figure out that, technically, `u32` would be the more fitting type
for the prices here, since they can't be negative? If so, kudos!""" for the inputs of the functions here, since the original prices shouldn't be negative? If so, kudos!"""
[[exercises]] [[exercises]]
name = "functions5" name = "functions5"
@ -248,7 +248,7 @@ starting and ending (plus one) indices of the items in the `Array` that you
want to end up in the slice. want to end up in the slice.
If you're curious why the first argument of `assert_eq!` does not have an If you're curious why the first argument of `assert_eq!` does not have an
ampersand for a reference since the second argument is areference, take a look ampersand for a reference since the second argument is a reference, take a look
at the coercion chapter of the nomicon: at the coercion chapter of the nomicon:
https://doc.rust-lang.org/nomicon/coercions.html""" https://doc.rust-lang.org/nomicon/coercions.html"""

View file

@ -137,7 +137,7 @@ fi
Path=${1:-rustlings/} Path=${1:-rustlings/}
echo "Cloning Rustlings at $Path..." echo "Cloning Rustlings at $Path..."
git clone -q https://github.com/rust-lang/rustlings "$Path" git clone -q https://github.com/rust-lang/rustlings.git "$Path"
cd "$Path" cd "$Path"

View file

@ -1,19 +1,33 @@
use regex::Regex;
use serde::Deserialize; use serde::Deserialize;
use std::env;
use std::fmt::{self, Display, Formatter}; use std::fmt::{self, Display, Formatter};
use std::fs::{self, remove_file, File}; use std::fs::{self, remove_file, File};
use std::io::Read; use std::io::{self, BufRead, BufReader};
use std::path::PathBuf; use std::path::PathBuf;
use std::process::{self, Command}; use std::process::{self, exit, Command, Stdio};
use std::{array, env, mem};
use winnow::ascii::{space0, Caseless};
use winnow::combinator::opt;
use winnow::Parser;
const RUSTC_COLOR_ARGS: &[&str] = &["--color", "always"]; const RUSTC_COLOR_ARGS: &[&str] = &["--color", "always"];
const RUSTC_EDITION_ARGS: &[&str] = &["--edition", "2021"]; const RUSTC_EDITION_ARGS: &[&str] = &["--edition", "2021"];
const RUSTC_NO_DEBUG_ARGS: &[&str] = &["-C", "strip=debuginfo"]; const RUSTC_NO_DEBUG_ARGS: &[&str] = &["-C", "strip=debuginfo"];
const I_AM_DONE_REGEX: &str = r"(?m)^\s*///?\s*I\s+AM\s+NOT\s+DONE";
const CONTEXT: usize = 2; const CONTEXT: usize = 2;
const CLIPPY_CARGO_TOML_PATH: &str = "./exercises/22_clippy/Cargo.toml"; const CLIPPY_CARGO_TOML_PATH: &str = "./exercises/22_clippy/Cargo.toml";
// Checks if the line contains the "I AM NOT DONE" comment.
fn contains_not_done_comment(input: &str) -> bool {
(
space0::<_, ()>,
"//",
opt('/'),
space0,
Caseless("I AM NOT DONE"),
)
.parse_next(&mut &*input)
.is_ok()
}
// Get a temporary file name that is hopefully unique // Get a temporary file name that is hopefully unique
#[inline] #[inline]
fn temp_file() -> String { fn temp_file() -> String {
@ -58,7 +72,7 @@ pub struct Exercise {
// An enum to track of the state of an Exercise. // An enum to track of the state of an Exercise.
// An Exercise can be either Done or Pending // An Exercise can be either Done or Pending
#[derive(PartialEq, Debug)] #[derive(PartialEq, Eq, Debug)]
pub enum State { pub enum State {
// The state of the exercise once it's been completed // The state of the exercise once it's been completed
Done, Done,
@ -67,7 +81,7 @@ pub enum State {
} }
// The context information of a pending exercise // The context information of a pending exercise
#[derive(PartialEq, Debug)] #[derive(PartialEq, Eq, Debug)]
pub struct ContextLine { pub struct ContextLine {
// The source code that is still pending completion // The source code that is still pending completion
pub line: String, pub line: String,
@ -148,7 +162,10 @@ path = "{}.rs""#,
.args(RUSTC_COLOR_ARGS) .args(RUSTC_COLOR_ARGS)
.args(RUSTC_EDITION_ARGS) .args(RUSTC_EDITION_ARGS)
.args(RUSTC_NO_DEBUG_ARGS) .args(RUSTC_NO_DEBUG_ARGS)
.output() .stdin(Stdio::null())
.stdout(Stdio::null())
.stderr(Stdio::null())
.status()
.expect("Failed to compile!"); .expect("Failed to compile!");
// Due to an issue with Clippy, a cargo clean is required to catch all lints. // Due to an issue with Clippy, a cargo clean is required to catch all lints.
// See https://github.com/rust-lang/rust-clippy/issues/2604 // See https://github.com/rust-lang/rust-clippy/issues/2604
@ -157,7 +174,10 @@ path = "{}.rs""#,
Command::new("cargo") Command::new("cargo")
.args(["clean", "--manifest-path", CLIPPY_CARGO_TOML_PATH]) .args(["clean", "--manifest-path", CLIPPY_CARGO_TOML_PATH])
.args(RUSTC_COLOR_ARGS) .args(RUSTC_COLOR_ARGS)
.output() .stdin(Stdio::null())
.stdout(Stdio::null())
.stderr(Stdio::null())
.status()
.expect("Failed to run 'cargo clean'"); .expect("Failed to run 'cargo clean'");
Command::new("cargo") Command::new("cargo")
.args(["clippy", "--manifest-path", CLIPPY_CARGO_TOML_PATH]) .args(["clippy", "--manifest-path", CLIPPY_CARGO_TOML_PATH])
@ -205,51 +225,101 @@ path = "{}.rs""#,
} }
pub fn state(&self) -> State { pub fn state(&self) -> State {
let mut source_file = File::open(&self.path).unwrap_or_else(|e| { let source_file = File::open(&self.path).unwrap_or_else(|e| {
panic!( println!(
"We were unable to open the exercise file {}! {e}", "Failed to open the exercise file {}: {e}",
self.path.display() self.path.display(),
) );
exit(1);
}); });
let mut source_reader = BufReader::new(source_file);
let source = { // Read the next line into `buf` without the newline at the end.
let mut s = String::new(); let mut read_line = |buf: &mut String| -> io::Result<_> {
source_file.read_to_string(&mut s).unwrap_or_else(|e| { let n = source_reader.read_line(buf)?;
panic!( if buf.ends_with('\n') {
"We were unable to read the exercise file {}! {e}", buf.pop();
self.path.display() if buf.ends_with('\r') {
) buf.pop();
}); }
s }
Ok(n)
}; };
let re = Regex::new(I_AM_DONE_REGEX).unwrap(); let mut current_line_number: usize = 1;
// Keep the last `CONTEXT` lines while iterating over the file lines.
let mut prev_lines: [_; CONTEXT] = array::from_fn(|_| String::with_capacity(256));
let mut line = String::with_capacity(256);
if !re.is_match(&source) { loop {
return State::Done; let n = read_line(&mut line).unwrap_or_else(|e| {
println!(
"Failed to read the exercise file {}: {e}",
self.path.display(),
);
exit(1);
});
// Reached the end of the file and didn't find the comment.
if n == 0 {
return State::Done;
}
if contains_not_done_comment(&line) {
let mut context = Vec::with_capacity(2 * CONTEXT + 1);
// Previous lines.
for (ind, prev_line) in prev_lines
.into_iter()
.take(current_line_number - 1)
.enumerate()
.rev()
{
context.push(ContextLine {
line: prev_line,
number: current_line_number - 1 - ind,
important: false,
});
}
// Current line.
context.push(ContextLine {
line,
number: current_line_number,
important: true,
});
// Next lines.
for ind in 0..CONTEXT {
let mut next_line = String::with_capacity(256);
let Ok(n) = read_line(&mut next_line) else {
// If an error occurs, just ignore the next lines.
break;
};
// Reached the end of the file.
if n == 0 {
break;
}
context.push(ContextLine {
line: next_line,
number: current_line_number + 1 + ind,
important: false,
});
}
return State::Pending(context);
}
current_line_number += 1;
// Add the current line as a previous line and shift the older lines by one.
for prev_line in &mut prev_lines {
mem::swap(&mut line, prev_line);
}
// The current line now contains the oldest previous line.
// Recycle it for reading the next line.
line.clear();
} }
let matched_line_index = source
.lines()
.enumerate()
.find_map(|(i, line)| if re.is_match(line) { Some(i) } else { None })
.expect("This should not happen at all");
let min_line = ((matched_line_index as i32) - (CONTEXT as i32)).max(0) as usize;
let max_line = matched_line_index + CONTEXT;
let context = source
.lines()
.enumerate()
.filter(|&(i, _)| i >= min_line && i <= max_line)
.map(|(i, line)| ContextLine {
line: line.to_string(),
number: i + 1,
important: i == matched_line_index,
})
.collect();
State::Pending(context)
} }
// Check that the exercise looks to be solved using self.state() // Check that the exercise looks to be solved using self.state()
@ -375,4 +445,20 @@ mod test {
let out = exercise.compile().unwrap().run().unwrap(); let out = exercise.compile().unwrap().run().unwrap();
assert!(out.stdout.contains("THIS TEST TOO SHALL PASS")); assert!(out.stdout.contains("THIS TEST TOO SHALL PASS"));
} }
#[test]
fn test_not_done() {
assert!(contains_not_done_comment("// I AM NOT DONE"));
assert!(contains_not_done_comment("/// I AM NOT DONE"));
assert!(contains_not_done_comment("// I AM NOT DONE"));
assert!(contains_not_done_comment("/// I AM NOT DONE"));
assert!(contains_not_done_comment("// I AM NOT DONE "));
assert!(contains_not_done_comment("// I AM NOT DONE!"));
assert!(contains_not_done_comment("// I am not done"));
assert!(contains_not_done_comment("// i am NOT done"));
assert!(!contains_not_done_comment("I AM NOT DONE"));
assert!(!contains_not_done_comment("// NOT DONE"));
assert!(!contains_not_done_comment("DONE"));
}
} }

View file

@ -1,16 +1,18 @@
use crate::exercise::{Exercise, ExerciseList}; use crate::exercise::{Exercise, ExerciseList};
use crate::project::RustAnalyzerProject; use crate::project::write_project_json;
use crate::run::{reset, run}; use crate::run::{reset, run};
use crate::verify::verify; use crate::verify::verify;
use anyhow::Result;
use clap::{Parser, Subcommand}; use clap::{Parser, Subcommand};
use console::Emoji; use console::Emoji;
use notify_debouncer_mini::notify::{self, RecursiveMode}; use notify_debouncer_mini::notify::{self, RecursiveMode};
use notify_debouncer_mini::{new_debouncer, DebouncedEventKind}; use notify_debouncer_mini::{new_debouncer, DebouncedEventKind};
use shlex::Shlex;
use std::ffi::OsStr; use std::ffi::OsStr;
use std::fs; use std::fs;
use std::io::{self, prelude::*}; use std::io::{self, prelude::*};
use std::path::Path; use std::path::Path;
use std::process::{Command, Stdio}; use std::process::Command;
use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::mpsc::{channel, RecvTimeoutError}; use std::sync::mpsc::{channel, RecvTimeoutError};
use std::sync::{Arc, Mutex}; use std::sync::{Arc, Mutex};
@ -84,31 +86,32 @@ enum Subcommands {
Lsp, Lsp,
} }
fn main() { fn main() -> Result<()> {
let args = Args::parse(); let args = Args::parse();
if args.command.is_none() { if args.command.is_none() {
println!("\n{WELCOME}\n"); println!("\n{WELCOME}\n");
} }
if !Path::new("info.toml").exists() { if which::which("rustc").is_err() {
println!(
"{} must be run from the rustlings directory",
std::env::current_exe().unwrap().to_str().unwrap()
);
println!("Try `cd rustlings/`!");
std::process::exit(1);
}
if !rustc_exists() {
println!("We cannot find `rustc`."); println!("We cannot find `rustc`.");
println!("Try running `rustc --version` to diagnose your problem."); println!("Try running `rustc --version` to diagnose your problem.");
println!("For instructions on how to install Rust, check the README."); println!("For instructions on how to install Rust, check the README.");
std::process::exit(1); std::process::exit(1);
} }
let toml_str = &fs::read_to_string("info.toml").unwrap(); let info_file = fs::read_to_string("info.toml").unwrap_or_else(|e| {
let exercises = toml::from_str::<ExerciseList>(toml_str).unwrap().exercises; match e.kind() {
io::ErrorKind::NotFound => println!(
"The program must be run from the rustlings directory\nTry `cd rustlings/`!",
),
_ => println!("Failed to read the info.toml file: {e}"),
}
std::process::exit(1);
});
let exercises = toml_edit::de::from_str::<ExerciseList>(&info_file)
.unwrap()
.exercises;
let verbose = args.nocapture; let verbose = args.nocapture;
let command = args.command.unwrap_or_else(|| { let command = args.command.unwrap_or_else(|| {
@ -128,31 +131,43 @@ fn main() {
println!("{:<17}\t{:<46}\t{:<7}", "Name", "Path", "Status"); println!("{:<17}\t{:<46}\t{:<7}", "Name", "Path", "Status");
} }
let mut exercises_done: u16 = 0; let mut exercises_done: u16 = 0;
let filters = filter.clone().unwrap_or_default().to_lowercase(); let lowercase_filter = filter
exercises.iter().for_each(|e| { .as_ref()
let fname = format!("{}", e.path.display()); .map(|s| s.to_lowercase())
.unwrap_or_default();
let filters = lowercase_filter
.split(',')
.filter_map(|f| {
let f = f.trim();
if f.is_empty() {
None
} else {
Some(f)
}
})
.collect::<Vec<_>>();
for exercise in &exercises {
let fname = exercise.path.to_string_lossy();
let filter_cond = filters let filter_cond = filters
.split(',') .iter()
.filter(|f| !f.trim().is_empty()) .any(|f| exercise.name.contains(f) || fname.contains(f));
.any(|f| e.name.contains(f) || fname.contains(f)); let looks_done = exercise.looks_done();
let status = if e.looks_done() { let status = if looks_done {
exercises_done += 1; exercises_done += 1;
"Done" "Done"
} else { } else {
"Pending" "Pending"
}; };
let solve_cond = { let solve_cond =
(e.looks_done() && solved) (looks_done && solved) || (!looks_done && unsolved) || (!solved && !unsolved);
|| (!e.looks_done() && unsolved)
|| (!solved && !unsolved)
};
if solve_cond && (filter_cond || filter.is_none()) { if solve_cond && (filter_cond || filter.is_none()) {
let line = if paths { let line = if paths {
format!("{fname}\n") format!("{fname}\n")
} else if names { } else if names {
format!("{}\n", e.name) format!("{}\n", exercise.name)
} else { } else {
format!("{:<17}\t{fname:<46}\t{status:<7}\n", e.name) format!("{:<17}\t{fname:<46}\t{status:<7}\n", exercise.name)
}; };
// Somehow using println! leads to the binary panicking // Somehow using println! leads to the binary panicking
// when its output is piped. // when its output is piped.
@ -168,7 +183,8 @@ fn main() {
}); });
} }
} }
}); }
let percentage_progress = exercises_done as f32 / exercises.len() as f32 * 100.0; let percentage_progress = exercises_done as f32 / exercises.len() as f32 * 100.0;
println!( println!(
"Progress: You completed {} / {} exercises ({:.1} %).", "Progress: You completed {} / {} exercises ({:.1} %).",
@ -203,30 +219,17 @@ fn main() {
} }
Subcommands::Lsp => { Subcommands::Lsp => {
let mut project = RustAnalyzerProject::new(); if let Err(e) = write_project_json(exercises) {
project println!("Failed to write rust-project.json to disk for rust-analyzer: {e}");
.get_sysroot_src()
.expect("Couldn't find toolchain path, do you have `rustc` installed?");
project
.exercises_to_json()
.expect("Couldn't parse rustlings exercises files");
if project.crates.is_empty() {
println!("Failed find any exercises, make sure you're in the `rustlings` folder");
} else if project.write_to_disk().is_err() {
println!("Failed to write rust-project.json to disk for rust-analyzer");
} else { } else {
println!("Successfully generated rust-project.json"); println!("Successfully generated rust-project.json");
println!("rust-analyzer will now parse exercises, restart your language server or editor") println!("rust-analyzer will now parse exercises, restart your language server or editor");
} }
} }
Subcommands::Watch { success_hints } => match watch(&exercises, verbose, success_hints) { Subcommands::Watch { success_hints } => match watch(&exercises, verbose, success_hints) {
Err(e) => { Err(e) => {
println!( println!("Error: Could not watch your progress. Error message was {e:?}.");
"Error: Could not watch your progress. Error message was {:?}.",
e
);
println!("Most likely you've run out of disk space or your 'inotify limit' has been reached."); println!("Most likely you've run out of disk space or your 'inotify limit' has been reached.");
std::process::exit(1); std::process::exit(1);
} }
@ -243,56 +246,60 @@ fn main() {
} }
}, },
} }
Ok(())
} }
fn spawn_watch_shell( fn spawn_watch_shell(
failed_exercise_hint: &Arc<Mutex<Option<String>>>, failed_exercise_hint: Arc<Mutex<Option<String>>>,
should_quit: Arc<AtomicBool>, should_quit: Arc<AtomicBool>,
) { ) {
let failed_exercise_hint = Arc::clone(failed_exercise_hint);
println!("Welcome to watch mode! You can type 'help' to get an overview of the commands you can use here."); println!("Welcome to watch mode! You can type 'help' to get an overview of the commands you can use here.");
thread::spawn(move || loop {
let mut input = String::new(); thread::spawn(move || {
match io::stdin().read_line(&mut input) { let mut input = String::with_capacity(32);
Ok(_) => { let mut stdin = io::stdin().lock();
let input = input.trim();
if input == "hint" { loop {
if let Some(hint) = &*failed_exercise_hint.lock().unwrap() { // Recycle input buffer.
println!("{hint}"); input.clear();
}
} else if input == "clear" { if let Err(e) = stdin.read_line(&mut input) {
println!("\x1B[2J\x1B[1;1H"); println!("error reading command: {e}");
} else if input.eq("quit") { }
should_quit.store(true, Ordering::SeqCst);
println!("Bye!"); let input = input.trim();
} else if input.eq("help") { if input == "hint" {
println!("Commands available to you in watch mode:"); if let Some(hint) = &*failed_exercise_hint.lock().unwrap() {
println!(" hint - prints the current exercise's hint"); println!("{hint}");
println!(" clear - clears the screen"); }
println!(" quit - quits watch mode"); } else if input == "clear" {
println!(" !<cmd> - executes a command, like `!rustc --explain E0381`"); println!("\x1B[2J\x1B[1;1H");
println!(" help - displays this help message"); } else if input == "quit" {
println!(); should_quit.store(true, Ordering::SeqCst);
println!("Watch mode automatically re-evaluates the current exercise"); println!("Bye!");
println!("when you edit a file's contents.") } else if input == "help" {
} else if let Some(cmd) = input.strip_prefix('!') { println!("{WATCH_MODE_HELP_MESSAGE}");
let parts: Vec<&str> = cmd.split_whitespace().collect(); } else if let Some(cmd) = input.strip_prefix('!') {
if parts.is_empty() { let mut parts = Shlex::new(cmd);
println!("no command provided");
} else if let Err(e) = Command::new(parts[0]).args(&parts[1..]).status() { let Some(program) = parts.next() else {
println!("failed to execute command `{}`: {}", cmd, e); println!("no command provided");
} continue;
} else { };
println!("unknown command: {input}");
} if let Err(e) = Command::new(program).args(parts).status() {
println!("failed to execute command `{cmd}`: {e}");
}
} else {
println!("unknown command: {input}\n{WATCH_MODE_HELP_MESSAGE}");
} }
Err(error) => println!("error reading command: {error}"),
} }
}); });
} }
fn find_exercise<'a>(name: &str, exercises: &'a [Exercise]) -> &'a Exercise { fn find_exercise<'a>(name: &str, exercises: &'a [Exercise]) -> &'a Exercise {
if name.eq("next") { if name == "next" {
exercises exercises
.iter() .iter()
.find(|e| !e.looks_done()) .find(|e| !e.looks_done())
@ -338,7 +345,6 @@ fn watch(
clear_screen(); clear_screen();
let to_owned_hint = |t: &Exercise| t.hint.to_owned();
let failed_exercise_hint = match verify( let failed_exercise_hint = match verify(
exercises.iter(), exercises.iter(),
(0, exercises.len()), (0, exercises.len()),
@ -346,9 +352,9 @@ fn watch(
success_hints, success_hints,
) { ) {
Ok(_) => return Ok(WatchStatus::Finished), Ok(_) => return Ok(WatchStatus::Finished),
Err(exercise) => Arc::new(Mutex::new(Some(to_owned_hint(exercise)))), Err(exercise) => Arc::new(Mutex::new(Some(exercise.hint.clone()))),
}; };
spawn_watch_shell(&failed_exercise_hint, Arc::clone(&should_quit)); spawn_watch_shell(Arc::clone(&failed_exercise_hint), Arc::clone(&should_quit));
loop { loop {
match rx.recv_timeout(Duration::from_secs(1)) { match rx.recv_timeout(Duration::from_secs(1)) {
Ok(event) => match event { Ok(event) => match event {
@ -383,7 +389,7 @@ fn watch(
Err(exercise) => { Err(exercise) => {
let mut failed_exercise_hint = let mut failed_exercise_hint =
failed_exercise_hint.lock().unwrap(); failed_exercise_hint.lock().unwrap();
*failed_exercise_hint = Some(to_owned_hint(exercise)); *failed_exercise_hint = Some(exercise.hint.clone());
} }
} }
} }
@ -403,19 +409,7 @@ fn watch(
} }
} }
fn rustc_exists() -> bool { const DEFAULT_OUT: &str = "Thanks for installing Rustlings!
Command::new("rustc")
.args(["--version"])
.stdout(Stdio::null())
.stderr(Stdio::null())
.stdin(Stdio::null())
.spawn()
.and_then(|mut child| child.wait())
.map(|status| status.success())
.unwrap_or(false)
}
const DEFAULT_OUT: &str = r#"Thanks for installing Rustlings!
Is this your first time? Don't worry, Rustlings was made for beginners! We are Is this your first time? Don't worry, Rustlings was made for beginners! We are
going to teach you a lot of things about Rust, but before we can get going to teach you a lot of things about Rust, but before we can get
@ -441,7 +435,7 @@ started, here's a couple of notes about how Rustlings operates:
autocompletion, run the command `rustlings lsp`. autocompletion, run the command `rustlings lsp`.
Got all that? Great! To get started, run `rustlings watch` in order to get the first Got all that? Great! To get started, run `rustlings watch` in order to get the first
exercise. Make sure to have your editor open!"#; exercise. Make sure to have your editor open!";
const FENISH_LINE: &str = "+----------------------------------------------------+ const FENISH_LINE: &str = "+----------------------------------------------------+
| You made it to the Fe-nish line! | | You made it to the Fe-nish line! |
@ -477,3 +471,13 @@ const WELCOME: &str = r" welcome to...
| | | |_| \__ \ |_| | | | | | (_| \__ \ | | | |_| \__ \ |_| | | | | | (_| \__ \
|_| \__,_|___/\__|_|_|_| |_|\__, |___/ |_| \__,_|___/\__|_|_|_| |_|\__, |___/
|___/"; |___/";
const WATCH_MODE_HELP_MESSAGE: &str = "Commands available to you in watch mode:
hint - prints the current exercise's hint
clear - clears the screen
quit - quits watch mode
!<cmd> - executes a command, like `!rustc --explain E0381`
help - displays this help message
Watch mode automatically re-evaluates the current exercise
when you edit a file's contents.";

View file

@ -1,99 +1,83 @@
use glob::glob; use anyhow::{Context, Result};
use serde::{Deserialize, Serialize}; use serde::Serialize;
use std::env; use std::env;
use std::error::Error;
use std::path::PathBuf; use std::path::PathBuf;
use std::process::Command; use std::process::{Command, Stdio};
use crate::exercise::Exercise;
/// Contains the structure of resulting rust-project.json file /// Contains the structure of resulting rust-project.json file
/// and functions to build the data required to create the file /// and functions to build the data required to create the file
#[derive(Serialize, Deserialize)] #[derive(Serialize)]
pub struct RustAnalyzerProject { struct RustAnalyzerProject {
sysroot_src: String, sysroot_src: PathBuf,
pub crates: Vec<Crate>, crates: Vec<Crate>,
} }
#[derive(Serialize, Deserialize)] #[derive(Serialize)]
pub struct Crate { struct Crate {
root_module: String, root_module: PathBuf,
edition: String, edition: &'static str,
deps: Vec<String>, // Not used, but required in the JSON file.
cfg: Vec<String>, deps: Vec<()>,
// Only `test` is used for all crates.
// Therefore, an array is used instead of a `Vec`.
cfg: [&'static str; 1],
} }
impl RustAnalyzerProject { impl RustAnalyzerProject {
pub fn new() -> RustAnalyzerProject { fn build(exercises: Vec<Exercise>) -> Result<Self> {
RustAnalyzerProject { let crates = exercises
sysroot_src: String::new(), .into_iter()
crates: Vec::new(), .map(|exercise| Crate {
} root_module: exercise.path,
} edition: "2021",
deps: Vec::new(),
// This allows rust_analyzer to work inside `#[test]` blocks
cfg: ["test"],
})
.collect();
/// Write rust-project.json to disk if let Some(path) = env::var_os("RUST_SRC_PATH") {
pub fn write_to_disk(&self) -> Result<(), std::io::Error> { return Ok(Self {
std::fs::write( sysroot_src: PathBuf::from(path),
"./rust-project.json", crates,
serde_json::to_vec(&self).expect("Failed to serialize to JSON"), });
)?;
Ok(())
}
/// If path contains .rs extension, add a crate to `rust-project.json`
fn path_to_json(&mut self, path: PathBuf) -> Result<(), Box<dyn Error>> {
if let Some(ext) = path.extension() {
if ext == "rs" {
self.crates.push(Crate {
root_module: path.display().to_string(),
edition: "2021".to_string(),
deps: Vec::new(),
// This allows rust_analyzer to work inside #[test] blocks
cfg: vec!["test".to_string()],
})
}
}
Ok(())
}
/// Parse the exercises folder for .rs files, any matches will create
/// a new `crate` in rust-project.json which allows rust-analyzer to
/// treat it like a normal binary
pub fn exercises_to_json(&mut self) -> Result<(), Box<dyn Error>> {
for path in glob("./exercises/**/*")? {
self.path_to_json(path?)?;
}
Ok(())
}
/// Use `rustc` to determine the default toolchain
pub fn get_sysroot_src(&mut self) -> Result<(), Box<dyn Error>> {
// check if RUST_SRC_PATH is set
if let Ok(path) = env::var("RUST_SRC_PATH") {
self.sysroot_src = path;
return Ok(());
} }
let toolchain = Command::new("rustc") let toolchain = Command::new("rustc")
.arg("--print") .arg("--print")
.arg("sysroot") .arg("sysroot")
.output()? .stderr(Stdio::inherit())
.output()
.context("Failed to get the sysroot from `rustc`. Do you have `rustc` installed?")?
.stdout; .stdout;
let toolchain = String::from_utf8_lossy(&toolchain); let toolchain =
let mut whitespace_iter = toolchain.split_whitespace(); String::from_utf8(toolchain).context("The toolchain path is invalid UTF8")?;
let toolchain = toolchain.trim_end();
println!("Determined toolchain: {toolchain}\n");
let toolchain = whitespace_iter.next().unwrap_or(&toolchain); let mut sysroot_src = PathBuf::with_capacity(256);
sysroot_src.extend([toolchain, "lib", "rustlib", "src", "rust", "library"]);
println!("Determined toolchain: {}\n", &toolchain); Ok(Self {
sysroot_src,
self.sysroot_src = (std::path::Path::new(toolchain) crates,
.join("lib") })
.join("rustlib")
.join("src")
.join("rust")
.join("library")
.to_string_lossy())
.to_string();
Ok(())
} }
} }
/// Write `rust-project.json` to disk.
pub fn write_project_json(exercises: Vec<Exercise>) -> Result<()> {
let content = RustAnalyzerProject::build(exercises)?;
// Using the capacity 2^14 since the file length in bytes is higher than 2^13.
// The final length is not known exactly because it depends on the user's sysroot path,
// the current number of exercises etc.
let mut buf = Vec::with_capacity(1 << 14);
serde_json::to_writer(&mut buf, &content)?;
std::fs::write("rust-project.json", buf)?;
Ok(())
}

View file

@ -21,7 +21,8 @@ pub fn run(exercise: &Exercise, verbose: bool) -> Result<(), ()> {
// Resets the exercise by stashing the changes. // Resets the exercise by stashing the changes.
pub fn reset(exercise: &Exercise) -> Result<(), ()> { pub fn reset(exercise: &Exercise) -> Result<(), ()> {
let command = Command::new("git") let command = Command::new("git")
.args(["stash", "--"]) .arg("stash")
.arg("--")
.arg(&exercise.path) .arg(&exercise.path)
.spawn(); .spawn();

View file

@ -1,33 +1,28 @@
macro_rules! warn { macro_rules! print_emoji {
($fmt:literal, $ex:expr) => {{ ($emoji:expr, $sign:expr, $color: ident, $fmt:literal, $ex:expr) => {{
use console::{style, Emoji}; use console::{style, Emoji};
use std::env; use std::env;
let formatstr = format!($fmt, $ex); let formatstr = format!($fmt, $ex);
if env::var("NO_EMOJI").is_ok() { if env::var("NO_EMOJI").is_ok() {
println!("{} {}", style("!").red(), style(formatstr).red()); println!("{} {}", style($sign).$color(), style(formatstr).$color());
} else { } else {
println!( println!(
"{} {}", "{} {}",
style(Emoji("⚠️ ", "!")).red(), style(Emoji($emoji, $sign)).$color(),
style(formatstr).red() style(formatstr).$color()
); );
} }
}}; }};
} }
macro_rules! success { macro_rules! warn {
($fmt:literal, $ex:expr) => {{ ($fmt:literal, $ex:expr) => {{
use console::{style, Emoji}; print_emoji!("⚠️ ", "!", red, $fmt, $ex);
use std::env; }};
let formatstr = format!($fmt, $ex); }
if env::var("NO_EMOJI").is_ok() {
println!("{} {}", style("").green(), style(formatstr).green()); macro_rules! success {
} else { ($fmt:literal, $ex:expr) => {{
println!( print_emoji!("", "", green, $fmt, $ex);
"{} {}",
style(Emoji("", "")).green(),
style(formatstr).green()
);
}
}}; }};
} }

View file

@ -24,7 +24,7 @@ pub fn verify<'a>(
.progress_chars("#>-"), .progress_chars("#>-"),
); );
bar.set_position(num_done as u64); bar.set_position(num_done as u64);
bar.set_message(format!("({:.1} %)", percentage)); bar.set_message(format!("({percentage:.1} %)"));
for exercise in exercises { for exercise in exercises {
let compile_result = match exercise.mode { let compile_result = match exercise.mode {
@ -37,11 +37,21 @@ pub fn verify<'a>(
} }
percentage += 100.0 / total as f32; percentage += 100.0 / total as f32;
bar.inc(1); bar.inc(1);
bar.set_message(format!("({:.1} %)", percentage)); bar.set_message(format!("({percentage:.1} %)"));
if bar.position() == total as u64 {
println!(
"Progress: You completed {} / {} exercises ({:.1} %).",
bar.position(),
total,
percentage
);
bar.finish();
}
} }
Ok(()) Ok(())
} }
#[derive(PartialEq, Eq)]
enum RunMode { enum RunMode {
Interactive, Interactive,
NonInteractive, NonInteractive,
@ -115,7 +125,7 @@ fn compile_and_test(
if verbose { if verbose {
println!("{}", output.stdout); println!("{}", output.stdout);
} }
if let RunMode::Interactive = run_mode { if run_mode == RunMode::Interactive {
Ok(prompt_for_completion(exercise, None, success_hints)) Ok(prompt_for_completion(exercise, None, success_hints))
} else { } else {
Ok(true) Ok(true)
@ -182,27 +192,25 @@ fn prompt_for_completion(
Mode::Test => "The code is compiling, and the tests pass!", Mode::Test => "The code is compiling, and the tests pass!",
Mode::Clippy => clippy_success_msg, Mode::Clippy => clippy_success_msg,
}; };
println!();
if no_emoji { if no_emoji {
println!("~*~ {success_msg} ~*~") println!("\n~*~ {success_msg} ~*~\n");
} else { } else {
println!("🎉 🎉 {success_msg} 🎉 🎉") println!("\n🎉 🎉 {success_msg} 🎉 🎉\n");
} }
println!();
if let Some(output) = prompt_output { if let Some(output) = prompt_output {
println!("Output:"); println!(
println!("{}", separator()); "Output:\n{separator}\n{output}\n{separator}\n",
println!("{output}"); separator = separator(),
println!("{}", separator()); );
println!();
} }
if success_hints { if success_hints {
println!("Hints:"); println!(
println!("{}", separator()); "Hints:\n{separator}\n{}\n{separator}\n",
println!("{}", exercise.hint); exercise.hint,
println!("{}", separator()); separator = separator(),
println!(); );
} }
println!("You can keep working on this exercise,"); println!("You can keep working on this exercise,");
@ -215,14 +223,14 @@ fn prompt_for_completion(
let formatted_line = if context_line.important { let formatted_line = if context_line.important {
format!("{}", style(context_line.line).bold()) format!("{}", style(context_line.line).bold())
} else { } else {
context_line.line.to_string() context_line.line
}; };
println!( println!(
"{:>2} {} {}", "{:>2} {} {}",
style(context_line.number).blue().bold(), style(context_line.number).blue().bold(),
style("|").blue(), style("|").blue(),
formatted_line formatted_line,
); );
} }