mirror of
https://github.com/rust-lang/rustlings.git
synced 2025-01-09 20:03:24 +03:00
Compare commits
18 commits
dc5c72bc19
...
aedeff8b24
Author | SHA1 | Date | |
---|---|---|---|
aedeff8b24 | |||
75e2804c83 | |||
c45d2c3255 | |||
ea40804371 | |||
ee2b772dd5 | |||
62a2c1a6d9 | |||
de0befef9c | |||
5658998c0c | |||
89e0f64279 | |||
edea76b5b9 | |||
016e6a014e | |||
cdeb8ce229 | |||
12504b01e9 | |||
c3a92b1248 | |||
181c81f016 | |||
cb7ce006b5 | |||
2150d629b1 | |||
c82c367324 |
117
Cargo.lock
generated
117
Cargo.lock
generated
|
@ -271,16 +271,6 @@ version = "1.0.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
|
checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "errno"
|
|
||||||
version = "0.3.8"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245"
|
|
||||||
dependencies = [
|
|
||||||
"libc",
|
|
||||||
"windows-sys 0.52.0",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "filetime"
|
name = "filetime"
|
||||||
version = "0.2.23"
|
version = "0.2.23"
|
||||||
|
@ -289,7 +279,7 @@ checksum = "1ee447700ac8aa0b2f2bd7bc4462ad686ba06baa6727ac149a2d6277f0d240fd"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"libc",
|
"libc",
|
||||||
"redox_syscall",
|
"redox_syscall 0.4.1",
|
||||||
"windows-sys 0.52.0",
|
"windows-sys 0.52.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -333,15 +323,6 @@ 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 = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
|
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "home"
|
|
||||||
version = "0.5.9"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5"
|
|
||||||
dependencies = [
|
|
||||||
"windows-sys 0.52.0",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "indexmap"
|
name = "indexmap"
|
||||||
version = "2.2.6"
|
version = "2.2.6"
|
||||||
|
@ -419,17 +400,11 @@ 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 = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd"
|
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 = "lock_api"
|
name = "lock_api"
|
||||||
version = "0.4.11"
|
version = "0.4.12"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45"
|
checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"autocfg",
|
"autocfg",
|
||||||
"scopeguard",
|
"scopeguard",
|
||||||
|
@ -499,7 +474,6 @@ 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 = "5d40b221972a1fc5ef4d858a2f671fb34c75983eb385463dff3780eeff6a9d43"
|
checksum = "5d40b221972a1fc5ef4d858a2f671fb34c75983eb385463dff3780eeff6a9d43"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"crossbeam-channel",
|
|
||||||
"log",
|
"log",
|
||||||
"notify",
|
"notify",
|
||||||
]
|
]
|
||||||
|
@ -531,9 +505,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "parking_lot"
|
name = "parking_lot"
|
||||||
version = "0.12.1"
|
version = "0.12.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f"
|
checksum = "7e4af0ca4f6caed20e900d564c242b8e5d4903fdacf31d3daf527b66fe6f42fb"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"lock_api",
|
"lock_api",
|
||||||
"parking_lot_core",
|
"parking_lot_core",
|
||||||
|
@ -541,15 +515,15 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "parking_lot_core"
|
name = "parking_lot_core"
|
||||||
version = "0.9.9"
|
version = "0.9.10"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e"
|
checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"libc",
|
"libc",
|
||||||
"redox_syscall",
|
"redox_syscall 0.5.1",
|
||||||
"smallvec",
|
"smallvec",
|
||||||
"windows-targets 0.48.5",
|
"windows-targets 0.52.5",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -635,6 +609,15 @@ dependencies = [
|
||||||
"bitflags 1.3.2",
|
"bitflags 1.3.2",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "redox_syscall"
|
||||||
|
version = "0.5.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "469052894dcb553421e483e4209ee581a45100d31b4018de03e5a7ad86374a7e"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags 2.5.0",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "regex"
|
name = "regex"
|
||||||
version = "1.10.4"
|
version = "1.10.4"
|
||||||
|
@ -664,22 +647,9 @@ 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 = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56"
|
checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "rustix"
|
|
||||||
version = "0.38.34"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f"
|
|
||||||
dependencies = [
|
|
||||||
"bitflags 2.5.0",
|
|
||||||
"errno",
|
|
||||||
"libc",
|
|
||||||
"linux-raw-sys",
|
|
||||||
"windows-sys 0.52.0",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustlings"
|
name = "rustlings"
|
||||||
version = "6.0.0-beta.3"
|
version = "6.0.0-beta.5"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"assert_cmd",
|
"assert_cmd",
|
||||||
|
@ -692,13 +662,13 @@ dependencies = [
|
||||||
"ratatui",
|
"ratatui",
|
||||||
"rustlings-macros",
|
"rustlings-macros",
|
||||||
"serde",
|
"serde",
|
||||||
|
"serde_json",
|
||||||
"toml_edit",
|
"toml_edit",
|
||||||
"which",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustlings-macros"
|
name = "rustlings-macros"
|
||||||
version = "6.0.0-beta.3"
|
version = "6.0.0-beta.5"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"quote",
|
"quote",
|
||||||
"serde",
|
"serde",
|
||||||
|
@ -734,24 +704,35 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde"
|
name = "serde"
|
||||||
version = "1.0.198"
|
version = "1.0.199"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9846a40c979031340571da2545a4e5b7c4163bdae79b301d5f86d03979451fcc"
|
checksum = "0c9f6e76df036c77cd94996771fb40db98187f096dd0b9af39c6c6e452ba966a"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"serde_derive",
|
"serde_derive",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_derive"
|
name = "serde_derive"
|
||||||
version = "1.0.198"
|
version = "1.0.199"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e88edab869b01783ba905e7d0153f9fc1a6505a96e4ad3018011eedb838566d9"
|
checksum = "11bd257a6541e141e42ca6d24ae26f7714887b47e89aa739099104c7e4d3b7fc"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde_json"
|
||||||
|
version = "1.0.116"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3e17db7126d17feb94eb3fad46bf1a96b034e8aacbc2e775fe81505f8b0b2813"
|
||||||
|
dependencies = [
|
||||||
|
"itoa",
|
||||||
|
"ryu",
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_spanned"
|
name = "serde_spanned"
|
||||||
version = "0.6.5"
|
version = "0.6.5"
|
||||||
|
@ -894,9 +875,9 @@ checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "unicode-width"
|
name = "unicode-width"
|
||||||
version = "0.1.11"
|
version = "0.1.12"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e51733f11c9c4f72aa0c160008246859e340b00807569a0da0e7a1079b27ba85"
|
checksum = "68f5e5f3158ecfd4b8ff6fe086db7c8467a2dfdac97fe420f2b7c4aa97af66d6"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "utf8parse"
|
name = "utf8parse"
|
||||||
|
@ -935,18 +916,6 @@ 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"
|
||||||
|
@ -1119,19 +1088,13 @@ checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "winnow"
|
name = "winnow"
|
||||||
version = "0.6.6"
|
version = "0.6.7"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f0c976aaaa0e1f90dbb21e9587cdaf1d9679a1cde8875c0d6bd83ab96a208352"
|
checksum = "14b9415ee827af173ebb3f15f9083df5a122eb93572ec28741fb153356ea2578"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"memchr",
|
"memchr",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "winsafe"
|
|
||||||
version = "0.0.19"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "d135d17ab770252ad95e9a872d365cf3090e3be864a34ab46f48555993efc904"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "zerocopy"
|
name = "zerocopy"
|
||||||
version = "0.7.32"
|
version = "0.7.32"
|
||||||
|
|
13
Cargo.toml
13
Cargo.toml
|
@ -8,7 +8,7 @@ exclude = [
|
||||||
]
|
]
|
||||||
|
|
||||||
[workspace.package]
|
[workspace.package]
|
||||||
version = "6.0.0-beta.3"
|
version = "6.0.0-beta.5"
|
||||||
authors = [
|
authors = [
|
||||||
"Liv <mokou@fastmail.com>",
|
"Liv <mokou@fastmail.com>",
|
||||||
"Mo Bitar <mo8it@proton.me>",
|
"Mo Bitar <mo8it@proton.me>",
|
||||||
|
@ -20,13 +20,12 @@ license = "MIT"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
[workspace.dependencies]
|
[workspace.dependencies]
|
||||||
serde = { version = "1.0.198", features = ["derive"] }
|
serde = { version = "1.0.199", features = ["derive"] }
|
||||||
toml_edit = { version = "0.22.12", default-features = false, features = ["parse", "serde"] }
|
toml_edit = { version = "0.22.12", default-features = false, features = ["parse", "serde"] }
|
||||||
|
|
||||||
[package]
|
[package]
|
||||||
name = "rustlings"
|
name = "rustlings"
|
||||||
description = "Small exercises to get you used to reading and writing Rust code!"
|
description = "Small exercises to get you used to reading and writing Rust code!"
|
||||||
default-run = "rustlings"
|
|
||||||
version.workspace = true
|
version.workspace = true
|
||||||
authors.workspace = true
|
authors.workspace = true
|
||||||
repository.workspace = true
|
repository.workspace = true
|
||||||
|
@ -52,13 +51,13 @@ anyhow = "1.0.82"
|
||||||
clap = { version = "4.5.4", features = ["derive"] }
|
clap = { version = "4.5.4", features = ["derive"] }
|
||||||
crossterm = "0.27.0"
|
crossterm = "0.27.0"
|
||||||
hashbrown = "0.14.3"
|
hashbrown = "0.14.3"
|
||||||
notify-debouncer-mini = "0.4.1"
|
notify-debouncer-mini = { version = "0.4.1", default-features = false }
|
||||||
os_pipe = "1.1.5"
|
os_pipe = "1.1.5"
|
||||||
ratatui = "0.26.2"
|
ratatui = { version = "0.26.2", default-features = false, features = ["crossterm"] }
|
||||||
rustlings-macros = { path = "rustlings-macros", version = "=6.0.0-beta.3" }
|
rustlings-macros = { path = "rustlings-macros", version = "=6.0.0-beta.5" }
|
||||||
|
serde_json = "1.0.116"
|
||||||
serde.workspace = true
|
serde.workspace = true
|
||||||
toml_edit.workspace = true
|
toml_edit.workspace = true
|
||||||
which = "6.0.1"
|
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
assert_cmd = "2.0.14"
|
assert_cmd = "2.0.14"
|
||||||
|
|
|
@ -35,7 +35,7 @@ The following command will download and compile Rustlings:
|
||||||
<!-- TODO: Remove @6.0.0-beta.x -->
|
<!-- TODO: Remove @6.0.0-beta.x -->
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cargo install rustlings@6.0.0-beta.3
|
cargo install rustlings@6.0.0-beta.5
|
||||||
```
|
```
|
||||||
|
|
||||||
<details>
|
<details>
|
||||||
|
@ -44,7 +44,7 @@ cargo install rustlings@6.0.0-beta.3
|
||||||
<!-- TODO: Remove @6.0.0-beta.x -->
|
<!-- TODO: Remove @6.0.0-beta.x -->
|
||||||
|
|
||||||
- Make sure you have the latest Rust version by running `rustup update`
|
- Make sure you have the latest Rust version by running `rustup update`
|
||||||
- Try adding the `--locked` flag: `cargo install rustlings@6.0.0-beta.3 --locked`
|
- Try adding the `--locked` flag: `cargo install rustlings@6.0.0-beta.5 --locked`
|
||||||
- Otherwise, please [report the issue](https://github.com/rust-lang/rustlings/issues/new)
|
- Otherwise, please [report the issue](https://github.com/rust-lang/rustlings/issues/new)
|
||||||
|
|
||||||
</details>
|
</details>
|
||||||
|
@ -83,7 +83,7 @@ It will rerun the current exercise automatically every time you change the exerc
|
||||||
<details>
|
<details>
|
||||||
<summary><strong>If detecting file changes in the <code>exercises/</code> directory fails…</strong> (<em>click to expand</em>)</summary>
|
<summary><strong>If detecting file changes in the <code>exercises/</code> directory fails…</strong> (<em>click to expand</em>)</summary>
|
||||||
|
|
||||||
> You can add the **`--manual-run`** flag (`rustlings --manual-run`) to manually rerun the current exercise by entering `r` or `run` in the watch mode.
|
> You can add the **`--manual-run`** flag (`rustlings --manual-run`) to manually rerun the current exercise by entering `r` (or `run`) in the watch mode.
|
||||||
>
|
>
|
||||||
> Please [report the issue](https://github.com/rust-lang/rustlings/issues/new) with some information about your operating system and whether you run Rustlings in a container or virtual machine (e.g. WSL).
|
> Please [report the issue](https://github.com/rust-lang/rustlings/issues/new) with some information about your operating system and whether you run Rustlings in a container or virtual machine (e.g. WSL).
|
||||||
|
|
||||||
|
@ -91,7 +91,7 @@ It will rerun the current exercise automatically every time you change the exerc
|
||||||
|
|
||||||
### Exercise List
|
### Exercise List
|
||||||
|
|
||||||
In the [watch mode](#watch-mode) (after launching `rustlings`), you can enter `l` or `list` to open the interactive exercise list.
|
In the [watch mode](#watch-mode) (after launching `rustlings`), you can enter `l` (or `list`) to open the interactive exercise list.
|
||||||
|
|
||||||
The list allows you to…
|
The list allows you to…
|
||||||
|
|
||||||
|
|
|
@ -1,11 +1,10 @@
|
||||||
// We sometimes encourage you to keep trying things on a given exercise, even
|
// We sometimes encourage you to keep trying things on a given exercise, even
|
||||||
// after you already figured it out. If you got everything working and feel
|
// after you already figured it out. If you got everything working and feel
|
||||||
// ready for the next exercise, remove the `I AM NOT DONE` comment below.
|
// ready for the next exercise, enter `n` (or `next`) in the terminal.
|
||||||
//
|
//
|
||||||
// If you're running this using `rustlings watch`: The exercise file will be
|
// The exercise file will be reloaded when you change one of the lines below!
|
||||||
// reloaded when you change one of the lines below! Try adding a `println!`
|
// Try adding a new `println!`.
|
||||||
// line, or try changing what it outputs in your terminal. Try removing a
|
// Try removing a semicolon and see what happens in the terminal!
|
||||||
// semicolon and see what happens!
|
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
println!("Hello and");
|
println!("Hello and");
|
||||||
|
@ -22,5 +21,6 @@ fn main() {
|
||||||
println!("solve the exercises. Good luck!");
|
println!("solve the exercises. Good luck!");
|
||||||
println!();
|
println!();
|
||||||
println!("The file of this exercise is `exercises/00_intro/intro1.rs`. Have a look!");
|
println!("The file of this exercise is `exercises/00_intro/intro1.rs`. Have a look!");
|
||||||
println!("The current exercise path is shown under the progress bar in the watch mode.");
|
println!("The current exercise path will be always shown under the progress bar.");
|
||||||
|
println!("You can click on the path to open the exercise file in your editor.");
|
||||||
}
|
}
|
||||||
|
|
40
info.toml
40
info.toml
|
@ -1,32 +1,26 @@
|
||||||
format_version = 1
|
format_version = 1
|
||||||
|
|
||||||
welcome_message = """Is this your first time? Don't worry, Rustlings was made for beginners! We are
|
welcome_message = """Is this your first time? Don't worry, Rustlings is made for beginners!
|
||||||
going to teach you a lot of things about Rust, but before we can get
|
We are going to teach you a lot of things about Rust, but before we can
|
||||||
started, here's a couple of notes about how Rustlings operates:
|
get started, here are some notes about how Rustlings operates:
|
||||||
|
|
||||||
1. The central concept behind Rustlings is that you solve exercises. These
|
1. The central concept behind Rustlings is that you solve exercises. These
|
||||||
exercises usually have some sort of syntax error in them, which will cause
|
exercises usually contain some compiler or logic errors which cause the
|
||||||
them to fail compilation or testing. Sometimes there's a logic error instead
|
exercise to fail compilation or testing. It's your job to find all errors
|
||||||
of a syntax error. No matter what error, it's your job to find it and fix it!
|
and fix them!
|
||||||
You'll know when you fixed it because then, the exercise will compile and
|
2. Make sure to have your editor open in the `rustlings/` directory. Rustlings
|
||||||
Rustlings will be able to move on to the next exercise.
|
will show you the path of the current exercise under the progress bar. Open
|
||||||
2. If you run Rustlings in watch mode (which we recommend), it'll automatically
|
the exercise file in your editor, fix errors and save the file. Rustlings will
|
||||||
start with the first exercise. Don't get confused by an error message popping
|
automatically detect the file change and rerun the exercise. If all errors are
|
||||||
up as soon as you run Rustlings! This is part of the exercise that you're
|
fixed, Rustlings will ask you to move on to the next exercise.
|
||||||
supposed to solve, so open the exercise file in an editor and start your
|
3. If you're stuck on an exercise, enter `h` (or `hint`) to show a hint.
|
||||||
detective work!
|
|
||||||
3. If you're stuck on an exercise, there is a helpful hint you can view by typing
|
|
||||||
'hint' (in watch mode), or running `rustlings hint exercise_name`.
|
|
||||||
4. If an exercise doesn't make sense to you, feel free to open an issue on GitHub!
|
4. If an exercise doesn't make sense to you, feel free to open an issue on GitHub!
|
||||||
(https://github.com/rust-lang/rustlings/issues/new). We look at every issue,
|
(https://github.com/rust-lang/rustlings). We look at every issue, and sometimes,
|
||||||
and sometimes, other learners do too so you can help each other out!
|
other learners do too so you can help each other out!
|
||||||
|
|
||||||
Got all that? Great! To get started, run `rustlings watch` in order to get the first exercise.
|
|
||||||
Make sure to have your editor open in the `rustlings` directory!
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
final_message = """We hope you enjoyed learning about the various aspects of Rust!
|
final_message = """We hope you enjoyed learning about the various aspects of Rust!
|
||||||
If you noticed any issues, please don't hesitate to report them to our repo.
|
If you noticed any issues, don't hesitate to report them on Github.
|
||||||
You can also contribute your own exercises to help the greater community!
|
You can also contribute your own exercises to help the greater community!
|
||||||
|
|
||||||
Before reporting an issue or contributing, please read our guidelines:
|
Before reporting an issue or contributing, please read our guidelines:
|
||||||
|
@ -41,9 +35,7 @@ name = "intro1"
|
||||||
dir = "00_intro"
|
dir = "00_intro"
|
||||||
test = false
|
test = false
|
||||||
# TODO: Fix hint
|
# TODO: Fix hint
|
||||||
hint = """
|
hint = """Enter `n` (or `next`) followed by ENTER to move on to the next exercise"""
|
||||||
Remove the `I AM NOT DONE` comment in the `exercises/intro00/intro1.rs` file
|
|
||||||
to move on to the next exercise."""
|
|
||||||
|
|
||||||
[[exercises]]
|
[[exercises]]
|
||||||
name = "intro2"
|
name = "intro2"
|
||||||
|
|
|
@ -4,10 +4,11 @@ use crossterm::{
|
||||||
terminal::{Clear, ClearType},
|
terminal::{Clear, ClearType},
|
||||||
ExecutableCommand,
|
ExecutableCommand,
|
||||||
};
|
};
|
||||||
|
use serde::Deserialize;
|
||||||
use std::{
|
use std::{
|
||||||
fs::{self, File},
|
fs::{self, File},
|
||||||
io::{Read, StdoutLock, Write},
|
io::{Read, StdoutLock, Write},
|
||||||
path::Path,
|
path::{Path, PathBuf},
|
||||||
process::{Command, Stdio},
|
process::{Command, Stdio},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -32,6 +33,11 @@ pub enum StateFileStatus {
|
||||||
NotRead,
|
NotRead,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
struct CargoMetadata {
|
||||||
|
target_directory: PathBuf,
|
||||||
|
}
|
||||||
|
|
||||||
pub struct AppState {
|
pub struct AppState {
|
||||||
current_exercise_ind: usize,
|
current_exercise_ind: usize,
|
||||||
exercises: Vec<Exercise>,
|
exercises: Vec<Exercise>,
|
||||||
|
@ -39,6 +45,7 @@ pub struct AppState {
|
||||||
final_message: String,
|
final_message: String,
|
||||||
file_buf: Vec<u8>,
|
file_buf: Vec<u8>,
|
||||||
official_exercises: bool,
|
official_exercises: bool,
|
||||||
|
target_dir: PathBuf,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AppState {
|
impl AppState {
|
||||||
|
@ -90,7 +97,24 @@ impl AppState {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
exercise_infos: Vec<ExerciseInfo>,
|
exercise_infos: Vec<ExerciseInfo>,
|
||||||
final_message: String,
|
final_message: String,
|
||||||
) -> (Self, StateFileStatus) {
|
) -> Result<(Self, StateFileStatus)> {
|
||||||
|
let metadata_output = Command::new("cargo")
|
||||||
|
.arg("metadata")
|
||||||
|
.arg("-q")
|
||||||
|
.arg("--format-version")
|
||||||
|
.arg("1")
|
||||||
|
.arg("--no-deps")
|
||||||
|
.stdin(Stdio::null())
|
||||||
|
.stderr(Stdio::inherit())
|
||||||
|
.output()
|
||||||
|
.context(CARGO_METADATA_ERR)?
|
||||||
|
.stdout;
|
||||||
|
let target_dir = serde_json::de::from_slice::<CargoMetadata>(&metadata_output)
|
||||||
|
.context(
|
||||||
|
"Failed to read the field `target_directory` from the `cargo metadata` output",
|
||||||
|
)?
|
||||||
|
.target_directory;
|
||||||
|
|
||||||
let exercises = exercise_infos
|
let exercises = exercise_infos
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|mut exercise_info| {
|
.map(|mut exercise_info| {
|
||||||
|
@ -127,11 +151,12 @@ impl AppState {
|
||||||
final_message,
|
final_message,
|
||||||
file_buf: Vec::with_capacity(2048),
|
file_buf: Vec::with_capacity(2048),
|
||||||
official_exercises: !Path::new("info.toml").exists(),
|
official_exercises: !Path::new("info.toml").exists(),
|
||||||
|
target_dir,
|
||||||
};
|
};
|
||||||
|
|
||||||
let state_file_status = slf.update_from_file();
|
let state_file_status = slf.update_from_file();
|
||||||
|
|
||||||
(slf, state_file_status)
|
Ok((slf, state_file_status))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
|
@ -154,6 +179,11 @@ impl AppState {
|
||||||
&self.exercises[self.current_exercise_ind]
|
&self.exercises[self.current_exercise_ind]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn target_dir(&self) -> &Path {
|
||||||
|
&self.target_dir
|
||||||
|
}
|
||||||
|
|
||||||
pub fn set_current_exercise_ind(&mut self, ind: usize) -> Result<()> {
|
pub fn set_current_exercise_ind(&mut self, ind: usize) -> Result<()> {
|
||||||
if ind >= self.exercises.len() {
|
if ind >= self.exercises.len() {
|
||||||
bail!(BAD_INDEX_ERR);
|
bail!(BAD_INDEX_ERR);
|
||||||
|
@ -313,7 +343,7 @@ impl AppState {
|
||||||
write!(writer, "Running {exercise} ... ")?;
|
write!(writer, "Running {exercise} ... ")?;
|
||||||
writer.flush()?;
|
writer.flush()?;
|
||||||
|
|
||||||
let success = exercise.run(&mut output)?;
|
let success = exercise.run(&mut output, &self.target_dir)?;
|
||||||
if !success {
|
if !success {
|
||||||
writeln!(writer, "{}\n", "FAILED".red())?;
|
writeln!(writer, "{}\n", "FAILED".red())?;
|
||||||
|
|
||||||
|
@ -380,6 +410,10 @@ impl AppState {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const CARGO_METADATA_ERR: &str = "Failed to run the command `cargo metadata …`
|
||||||
|
Did you already install Rust?
|
||||||
|
Try running `cargo --version` to diagnose the problem.";
|
||||||
|
|
||||||
const RERUNNING_ALL_EXERCISES_MSG: &[u8] = b"
|
const RERUNNING_ALL_EXERCISES_MSG: &[u8] = b"
|
||||||
All exercises seem to be done.
|
All exercises seem to be done.
|
||||||
Recompiling and running all exercises to make sure that all of them are actually done.
|
Recompiling and running all exercises to make sure that all of them are actually done.
|
||||||
|
|
70
src/cmd.rs
Normal file
70
src/cmd.rs
Normal file
|
@ -0,0 +1,70 @@
|
||||||
|
use anyhow::{Context, Result};
|
||||||
|
use std::{io::Read, path::Path, process::Command};
|
||||||
|
|
||||||
|
pub fn run_cmd(mut cmd: Command, description: &str, output: &mut Vec<u8>) -> Result<bool> {
|
||||||
|
let (mut reader, writer) = os_pipe::pipe()
|
||||||
|
.with_context(|| format!("Failed to create a pipe to run the command `{description}``"))?;
|
||||||
|
|
||||||
|
let writer_clone = writer.try_clone().with_context(|| {
|
||||||
|
format!("Failed to clone the pipe writer for the command `{description}`")
|
||||||
|
})?;
|
||||||
|
|
||||||
|
let mut handle = cmd
|
||||||
|
.stdout(writer_clone)
|
||||||
|
.stderr(writer)
|
||||||
|
.spawn()
|
||||||
|
.with_context(|| format!("Failed to run the command `{description}`"))?;
|
||||||
|
|
||||||
|
// Prevent pipe deadlock.
|
||||||
|
drop(cmd);
|
||||||
|
|
||||||
|
reader
|
||||||
|
.read_to_end(output)
|
||||||
|
.with_context(|| format!("Failed to read the output of the command `{description}`"))?;
|
||||||
|
|
||||||
|
output.push(b'\n');
|
||||||
|
|
||||||
|
handle
|
||||||
|
.wait()
|
||||||
|
.with_context(|| format!("Failed to wait on the command `{description}` to exit"))
|
||||||
|
.map(|status| status.success())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct CargoCmd<'a> {
|
||||||
|
pub subcommand: &'a str,
|
||||||
|
pub args: &'a [&'a str],
|
||||||
|
pub exercise_name: &'a str,
|
||||||
|
pub description: &'a str,
|
||||||
|
pub hide_warnings: bool,
|
||||||
|
pub target_dir: &'a Path,
|
||||||
|
pub output: &'a mut Vec<u8>,
|
||||||
|
pub dev: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> CargoCmd<'a> {
|
||||||
|
pub fn run(&mut self) -> Result<bool> {
|
||||||
|
let mut cmd = Command::new("cargo");
|
||||||
|
cmd.arg(self.subcommand);
|
||||||
|
|
||||||
|
// A hack to make `cargo run` work when developing Rustlings.
|
||||||
|
if self.dev {
|
||||||
|
cmd.arg("--manifest-path")
|
||||||
|
.arg("dev/Cargo.toml")
|
||||||
|
.arg("--target-dir")
|
||||||
|
.arg(self.target_dir);
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd.arg("--color")
|
||||||
|
.arg("always")
|
||||||
|
.arg("-q")
|
||||||
|
.arg("--bin")
|
||||||
|
.arg(self.exercise_name)
|
||||||
|
.args(self.args);
|
||||||
|
|
||||||
|
if self.hide_warnings {
|
||||||
|
cmd.env("RUSTFLAGS", "-A warnings");
|
||||||
|
}
|
||||||
|
|
||||||
|
run_cmd(cmd, self.description, self.output)
|
||||||
|
}
|
||||||
|
}
|
147
src/exercise.rs
147
src/exercise.rs
|
@ -1,57 +1,21 @@
|
||||||
use anyhow::{Context, Result};
|
use anyhow::Result;
|
||||||
use crossterm::style::{style, StyledContent, Stylize};
|
use crossterm::style::{style, StyledContent, Stylize};
|
||||||
use std::{
|
use std::{
|
||||||
fmt::{self, Display, Formatter},
|
fmt::{self, Display, Formatter},
|
||||||
io::{Read, Write},
|
io::Write,
|
||||||
process::{Command, Stdio},
|
path::{Path, PathBuf},
|
||||||
|
process::Command,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{in_official_repo, terminal_link::TerminalFileLink, DEBUG_PROFILE};
|
use crate::{
|
||||||
|
cmd::{run_cmd, CargoCmd},
|
||||||
|
in_official_repo,
|
||||||
|
terminal_link::TerminalFileLink,
|
||||||
|
DEBUG_PROFILE,
|
||||||
|
};
|
||||||
|
|
||||||
pub const OUTPUT_CAPACITY: usize = 1 << 14;
|
pub const OUTPUT_CAPACITY: usize = 1 << 14;
|
||||||
|
|
||||||
fn run_command(
|
|
||||||
mut cmd: Command,
|
|
||||||
cmd_description: &str,
|
|
||||||
output: &mut Vec<u8>,
|
|
||||||
stderr: bool,
|
|
||||||
) -> Result<bool> {
|
|
||||||
let (mut reader, writer) = os_pipe::pipe().with_context(|| {
|
|
||||||
format!("Failed to create a pipe to run the command `{cmd_description}``")
|
|
||||||
})?;
|
|
||||||
|
|
||||||
let (stdout, stderr) = if stderr {
|
|
||||||
(
|
|
||||||
Stdio::from(writer.try_clone().with_context(|| {
|
|
||||||
format!("Failed to clone the pipe writer for the command `{cmd_description}`")
|
|
||||||
})?),
|
|
||||||
Stdio::from(writer),
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
(Stdio::from(writer), Stdio::null())
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut handle = cmd
|
|
||||||
.stdout(stdout)
|
|
||||||
.stderr(stderr)
|
|
||||||
.spawn()
|
|
||||||
.with_context(|| format!("Failed to run the command `{cmd_description}`"))?;
|
|
||||||
|
|
||||||
// Prevent pipe deadlock.
|
|
||||||
drop(cmd);
|
|
||||||
|
|
||||||
reader
|
|
||||||
.read_to_end(output)
|
|
||||||
.with_context(|| format!("Failed to read the output of the command `{cmd_description}`"))?;
|
|
||||||
|
|
||||||
output.push(b'\n');
|
|
||||||
|
|
||||||
handle
|
|
||||||
.wait()
|
|
||||||
.with_context(|| format!("Failed to wait on the command `{cmd_description}` to exit"))
|
|
||||||
.map(|status| status.success())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct Exercise {
|
pub struct Exercise {
|
||||||
pub dir: Option<&'static str>,
|
pub dir: Option<&'static str>,
|
||||||
// Exercise's unique name
|
// Exercise's unique name
|
||||||
|
@ -66,11 +30,16 @@ pub struct Exercise {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Exercise {
|
impl Exercise {
|
||||||
fn run_bin(&self, output: &mut Vec<u8>) -> Result<bool> {
|
fn run_bin(&self, output: &mut Vec<u8>, target_dir: &Path) -> Result<bool> {
|
||||||
writeln!(output, "{}", "Output".underlined())?;
|
writeln!(output, "{}", "Output".underlined())?;
|
||||||
|
|
||||||
let bin_path = format!("target/debug/{}", self.name);
|
let mut bin_path =
|
||||||
let success = run_command(Command::new(&bin_path), &bin_path, output, true)?;
|
PathBuf::with_capacity(target_dir.as_os_str().len() + 7 + self.name.len());
|
||||||
|
bin_path.push(target_dir);
|
||||||
|
bin_path.push("debug");
|
||||||
|
bin_path.push(self.name);
|
||||||
|
|
||||||
|
let success = run_cmd(Command::new(&bin_path), &bin_path.to_string_lossy(), output)?;
|
||||||
|
|
||||||
if !success {
|
if !success {
|
||||||
writeln!(
|
writeln!(
|
||||||
|
@ -85,43 +54,23 @@ impl Exercise {
|
||||||
Ok(success)
|
Ok(success)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn cargo_cmd(
|
pub fn run(&self, output: &mut Vec<u8>, target_dir: &Path) -> Result<bool> {
|
||||||
&self,
|
|
||||||
command: &str,
|
|
||||||
args: &[&str],
|
|
||||||
cmd_description: &str,
|
|
||||||
output: &mut Vec<u8>,
|
|
||||||
dev: bool,
|
|
||||||
stderr: bool,
|
|
||||||
) -> Result<bool> {
|
|
||||||
let mut cmd = Command::new("cargo");
|
|
||||||
cmd.arg(command);
|
|
||||||
|
|
||||||
// A hack to make `cargo run` work when developing Rustlings.
|
|
||||||
if dev {
|
|
||||||
cmd.arg("--manifest-path")
|
|
||||||
.arg("dev/Cargo.toml")
|
|
||||||
.arg("--target-dir")
|
|
||||||
.arg("target");
|
|
||||||
}
|
|
||||||
|
|
||||||
cmd.arg("--color")
|
|
||||||
.arg("always")
|
|
||||||
.arg("-q")
|
|
||||||
.arg("--bin")
|
|
||||||
.arg(self.name)
|
|
||||||
.args(args);
|
|
||||||
|
|
||||||
run_command(cmd, cmd_description, output, stderr)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn run(&self, output: &mut Vec<u8>) -> Result<bool> {
|
|
||||||
output.clear();
|
output.clear();
|
||||||
|
|
||||||
// Developing the official Rustlings.
|
// Developing the official Rustlings.
|
||||||
let dev = DEBUG_PROFILE && in_official_repo();
|
let dev = DEBUG_PROFILE && in_official_repo();
|
||||||
|
|
||||||
let build_success = self.cargo_cmd("build", &[], "cargo build …", output, dev, true)?;
|
let build_success = CargoCmd {
|
||||||
|
subcommand: "build",
|
||||||
|
args: &[],
|
||||||
|
exercise_name: self.name,
|
||||||
|
description: "cargo build …",
|
||||||
|
hide_warnings: false,
|
||||||
|
target_dir,
|
||||||
|
output,
|
||||||
|
dev,
|
||||||
|
}
|
||||||
|
.run()?;
|
||||||
if !build_success {
|
if !build_success {
|
||||||
return Ok(false);
|
return Ok(false);
|
||||||
}
|
}
|
||||||
|
@ -134,34 +83,46 @@ impl Exercise {
|
||||||
} else {
|
} else {
|
||||||
&["--profile", "test"]
|
&["--profile", "test"]
|
||||||
};
|
};
|
||||||
let clippy_success =
|
let clippy_success = CargoCmd {
|
||||||
self.cargo_cmd("clippy", clippy_args, "cargo clippy …", output, dev, true)?;
|
subcommand: "clippy",
|
||||||
|
args: clippy_args,
|
||||||
|
exercise_name: self.name,
|
||||||
|
description: "cargo clippy …",
|
||||||
|
hide_warnings: false,
|
||||||
|
target_dir,
|
||||||
|
output,
|
||||||
|
dev,
|
||||||
|
}
|
||||||
|
.run()?;
|
||||||
if !clippy_success {
|
if !clippy_success {
|
||||||
return Ok(false);
|
return Ok(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
if !self.test {
|
if !self.test {
|
||||||
return self.run_bin(output);
|
return self.run_bin(output, target_dir);
|
||||||
}
|
}
|
||||||
|
|
||||||
let test_success = self.cargo_cmd(
|
let test_success = CargoCmd {
|
||||||
"test",
|
subcommand: "test",
|
||||||
&[
|
args: &[
|
||||||
"--",
|
"--",
|
||||||
"--color",
|
"--color",
|
||||||
"always",
|
"always",
|
||||||
"--nocapture",
|
"--show-output",
|
||||||
"--format",
|
"--format",
|
||||||
"pretty",
|
"pretty",
|
||||||
],
|
],
|
||||||
"cargo test …",
|
exercise_name: self.name,
|
||||||
|
description: "cargo test …",
|
||||||
|
// Hide warnings because they are shown by Clippy.
|
||||||
|
hide_warnings: true,
|
||||||
|
target_dir,
|
||||||
output,
|
output,
|
||||||
dev,
|
dev,
|
||||||
// Hide warnings because they are shown by Clippy.
|
}
|
||||||
false,
|
.run()?;
|
||||||
)?;
|
|
||||||
|
|
||||||
let run_success = self.run_bin(output)?;
|
let run_success = self.run_bin(output, target_dir)?;
|
||||||
|
|
||||||
Ok(test_success && run_success)
|
Ok(test_success && run_success)
|
||||||
}
|
}
|
||||||
|
|
|
@ -42,7 +42,7 @@ pub fn list(app_state: &mut AppState) -> Result<()> {
|
||||||
ui_state.message.clear();
|
ui_state.message.clear();
|
||||||
|
|
||||||
match key.code {
|
match key.code {
|
||||||
KeyCode::Char('q') => break,
|
KeyCode::Esc | KeyCode::Char('q') => break,
|
||||||
KeyCode::Down | KeyCode::Char('j') => ui_state.select_next(),
|
KeyCode::Down | KeyCode::Char('j') => ui_state.select_next(),
|
||||||
KeyCode::Up | KeyCode::Char('k') => ui_state.select_previous(),
|
KeyCode::Up | KeyCode::Char('k') => ui_state.select_previous(),
|
||||||
KeyCode::Home | KeyCode::Char('g') => ui_state.select_first(),
|
KeyCode::Home | KeyCode::Char('g') => ui_state.select_first(),
|
||||||
|
|
|
@ -194,7 +194,7 @@ impl<'a> UiState<'a> {
|
||||||
let message = if self.message.is_empty() {
|
let message = if self.message.is_empty() {
|
||||||
// Help footer.
|
// Help footer.
|
||||||
Span::raw(
|
Span::raw(
|
||||||
"↓/j ↑/k home/g end/G │ filter <d>one/<p>ending │ <r>eset │ <c>ontinue at │ <q>uit",
|
"↓/j ↑/k home/g end/G │ <c>ontinue at │ <r>eset │ filter <d>one/<p>ending │ <q>uit",
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
self.message.as_str().light_blue()
|
self.message.as_str().light_blue()
|
||||||
|
|
|
@ -15,6 +15,7 @@ use self::{app_state::AppState, dev::DevCommands, info_file::InfoFile, watch::Wa
|
||||||
|
|
||||||
mod app_state;
|
mod app_state;
|
||||||
mod cargo_toml;
|
mod cargo_toml;
|
||||||
|
mod cmd;
|
||||||
mod dev;
|
mod dev;
|
||||||
mod embedded;
|
mod embedded;
|
||||||
mod exercise;
|
mod exercise;
|
||||||
|
@ -86,8 +87,6 @@ fn main() -> Result<()> {
|
||||||
bail!("{OLD_METHOD_ERR}");
|
bail!("{OLD_METHOD_ERR}");
|
||||||
}
|
}
|
||||||
|
|
||||||
which::which("cargo").context(CARGO_NOT_FOUND_ERR)?;
|
|
||||||
|
|
||||||
match args.command {
|
match args.command {
|
||||||
Some(Subcommands::Init) => {
|
Some(Subcommands::Init) => {
|
||||||
if DEBUG_PROFILE {
|
if DEBUG_PROFILE {
|
||||||
|
@ -122,7 +121,7 @@ fn main() -> Result<()> {
|
||||||
let (mut app_state, state_file_status) = AppState::new(
|
let (mut app_state, state_file_status) = AppState::new(
|
||||||
info_file.exercises,
|
info_file.exercises,
|
||||||
info_file.final_message.unwrap_or_default(),
|
info_file.final_message.unwrap_or_default(),
|
||||||
);
|
)?;
|
||||||
|
|
||||||
if let Some(welcome_message) = info_file.welcome_message {
|
if let Some(welcome_message) = info_file.welcome_message {
|
||||||
match state_file_status {
|
match state_file_status {
|
||||||
|
@ -198,10 +197,6 @@ The new method doesn't include cloning the Rustlings' repository.
|
||||||
Please follow the instructions in the README:
|
Please follow the instructions in the README:
|
||||||
https://github.com/rust-lang/rustlings#getting-started";
|
https://github.com/rust-lang/rustlings#getting-started";
|
||||||
|
|
||||||
const CARGO_NOT_FOUND_ERR: &str = "Failed to find `cargo`.
|
|
||||||
Did you already install Rust?
|
|
||||||
Try running `cargo --version` to diagnose the problem.";
|
|
||||||
|
|
||||||
const FORMAT_VERSION_HIGHER_ERR: &str =
|
const FORMAT_VERSION_HIGHER_ERR: &str =
|
||||||
"The format version specified in the `info.toml` file is higher than the last one supported.
|
"The format version specified in the `info.toml` file is higher than the last one supported.
|
||||||
It is possible that you have an outdated version of Rustlings.
|
It is possible that you have an outdated version of Rustlings.
|
||||||
|
|
|
@ -11,7 +11,7 @@ use crate::{
|
||||||
pub fn run(app_state: &mut AppState) -> Result<()> {
|
pub fn run(app_state: &mut AppState) -> Result<()> {
|
||||||
let exercise = app_state.current_exercise();
|
let exercise = app_state.current_exercise();
|
||||||
let mut output = Vec::with_capacity(OUTPUT_CAPACITY);
|
let mut output = Vec::with_capacity(OUTPUT_CAPACITY);
|
||||||
let success = exercise.run(&mut output)?;
|
let success = exercise.run(&mut output, app_state.target_dir())?;
|
||||||
|
|
||||||
let mut stdout = io::stdout().lock();
|
let mut stdout = io::stdout().lock();
|
||||||
stdout.write_all(&output)?;
|
stdout.write_all(&output)?;
|
||||||
|
|
|
@ -124,5 +124,5 @@ The automatic detection of exercise file changes failed :(
|
||||||
Please try running `rustlings` again.
|
Please try running `rustlings` again.
|
||||||
|
|
||||||
If you keep getting this error, run `rustlings --manual-run` to deactivate the file watcher.
|
If you keep getting this error, run `rustlings --manual-run` to deactivate the file watcher.
|
||||||
You need to manually trigger running the current exercise using `r` or `run` then.
|
You need to manually trigger running the current exercise using `r` (or `run`) then.
|
||||||
";
|
";
|
||||||
|
|
|
@ -50,7 +50,10 @@ impl<'a> WatchState<'a> {
|
||||||
pub fn run_current_exercise(&mut self) -> Result<()> {
|
pub fn run_current_exercise(&mut self) -> Result<()> {
|
||||||
self.show_hint = false;
|
self.show_hint = false;
|
||||||
|
|
||||||
let success = self.app_state.current_exercise().run(&mut self.output)?;
|
let success = self
|
||||||
|
.app_state
|
||||||
|
.current_exercise()
|
||||||
|
.run(&mut self.output, self.app_state.target_dir())?;
|
||||||
if success {
|
if success {
|
||||||
self.done_status =
|
self.done_status =
|
||||||
if let Some(solution_path) = self.app_state.current_solution_path()? {
|
if let Some(solution_path) = self.app_state.current_solution_path()? {
|
||||||
|
@ -127,7 +130,7 @@ impl<'a> WatchState<'a> {
|
||||||
self.writer,
|
self.writer,
|
||||||
"{}\n",
|
"{}\n",
|
||||||
"Exercise done ✓
|
"Exercise done ✓
|
||||||
When you are done experimenting, enter `n` or `next` to go to the next exercise 🦀"
|
When you are done experimenting, enter `n` (or `next`) to move on to the next exercise 🦀"
|
||||||
.bold()
|
.bold()
|
||||||
.green(),
|
.green(),
|
||||||
)?;
|
)?;
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
use assert_cmd::prelude::*;
|
use assert_cmd::prelude::*;
|
||||||
use predicates::boolean::PredicateBooleanExt;
|
|
||||||
use std::process::Command;
|
use std::process::Command;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -110,8 +109,7 @@ fn run_compile_exercise_does_not_prompt() {
|
||||||
.args(["run", "pending_exercise"])
|
.args(["run", "pending_exercise"])
|
||||||
.current_dir("tests/fixture/state")
|
.current_dir("tests/fixture/state")
|
||||||
.assert()
|
.assert()
|
||||||
.code(0)
|
.code(0);
|
||||||
.stdout(predicates::str::contains("I AM NOT DONE").not());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -121,8 +119,7 @@ fn run_test_exercise_does_not_prompt() {
|
||||||
.args(["run", "pending_test_exercise"])
|
.args(["run", "pending_test_exercise"])
|
||||||
.current_dir("tests/fixture/state")
|
.current_dir("tests/fixture/state")
|
||||||
.assert()
|
.assert()
|
||||||
.code(0)
|
.code(0);
|
||||||
.stdout(predicates::str::contains("I AM NOT DONE").not());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
Loading…
Reference in a new issue