mirror of
https://github.com/rust-lang/rustlings.git
synced 2026-01-11 13:19:18 +00:00
Merge branch 'rust-lang:main' into main
This commit is contained in:
commit
c89613076b
@ -2001,6 +2001,141 @@
|
|||||||
"contributions": [
|
"contributions": [
|
||||||
"content"
|
"content"
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"login": "poneciak57",
|
||||||
|
"name": "Kacper Poneta",
|
||||||
|
"avatar_url": "https://avatars.githubusercontent.com/u/94321164?v=4",
|
||||||
|
"profile": "https://github.com/poneciak57",
|
||||||
|
"contributions": [
|
||||||
|
"content"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"login": "ktheory",
|
||||||
|
"name": "Aaron Suggs",
|
||||||
|
"avatar_url": "https://avatars.githubusercontent.com/u/975?v=4",
|
||||||
|
"profile": "https://ktheory.com/",
|
||||||
|
"contributions": [
|
||||||
|
"content"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"login": "alexwh",
|
||||||
|
"name": "Alex",
|
||||||
|
"avatar_url": "https://avatars.githubusercontent.com/u/1723612?v=4",
|
||||||
|
"profile": "https://github.com/alexwh",
|
||||||
|
"contributions": [
|
||||||
|
"content"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"login": "stornquist",
|
||||||
|
"name": "Sebastian Törnquist",
|
||||||
|
"avatar_url": "https://avatars.githubusercontent.com/u/42915664?v=4",
|
||||||
|
"profile": "https://github.com/stornquist",
|
||||||
|
"contributions": [
|
||||||
|
"content"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"login": "smlavine",
|
||||||
|
"name": "Sebastian LaVine",
|
||||||
|
"avatar_url": "https://avatars.githubusercontent.com/u/33563640?v=4",
|
||||||
|
"profile": "http://smlavine.com",
|
||||||
|
"contributions": [
|
||||||
|
"code"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"login": "akgerber",
|
||||||
|
"name": "Alan Gerber",
|
||||||
|
"avatar_url": "https://avatars.githubusercontent.com/u/201313?v=4",
|
||||||
|
"profile": "http://www.alangerber.us",
|
||||||
|
"contributions": [
|
||||||
|
"content"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"login": "esotuvaka",
|
||||||
|
"name": "Eric",
|
||||||
|
"avatar_url": "https://avatars.githubusercontent.com/u/104941850?v=4",
|
||||||
|
"profile": "http://esotuvaka.github.io",
|
||||||
|
"contributions": [
|
||||||
|
"content"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"login": "az0977776",
|
||||||
|
"name": "Aaron Wang",
|
||||||
|
"avatar_url": "https://avatars.githubusercontent.com/u/9172038?v=4",
|
||||||
|
"profile": "https://github.com/az0977776",
|
||||||
|
"contributions": [
|
||||||
|
"content"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"login": "nmay231",
|
||||||
|
"name": "Noah",
|
||||||
|
"avatar_url": "https://avatars.githubusercontent.com/u/35386821?v=4",
|
||||||
|
"profile": "https://github.com/nmay231",
|
||||||
|
"contributions": [
|
||||||
|
"content"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"login": "rb5014",
|
||||||
|
"name": "rb5014",
|
||||||
|
"avatar_url": "https://avatars.githubusercontent.com/u/105397317?v=4",
|
||||||
|
"profile": "https://github.com/rb5014",
|
||||||
|
"contributions": [
|
||||||
|
"content"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"login": "deedy5",
|
||||||
|
"name": "deedy5",
|
||||||
|
"avatar_url": "https://avatars.githubusercontent.com/u/65482418?v=4",
|
||||||
|
"profile": "https://github.com/deedy5",
|
||||||
|
"contributions": [
|
||||||
|
"content"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"login": "lionel-rowe",
|
||||||
|
"name": "lionel-rowe",
|
||||||
|
"avatar_url": "https://avatars.githubusercontent.com/u/26078826?v=4",
|
||||||
|
"profile": "https://github.com/lionel-rowe",
|
||||||
|
"contributions": [
|
||||||
|
"content"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"login": "Ben2917",
|
||||||
|
"name": "Ben",
|
||||||
|
"avatar_url": "https://avatars.githubusercontent.com/u/10279994?v=4",
|
||||||
|
"profile": "https://github.com/Ben2917",
|
||||||
|
"contributions": [
|
||||||
|
"content"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"login": "b1ue64",
|
||||||
|
"name": "b1ue64",
|
||||||
|
"avatar_url": "https://avatars.githubusercontent.com/u/77976308?v=4",
|
||||||
|
"profile": "https://github.com/b1ue64",
|
||||||
|
"contributions": [
|
||||||
|
"content"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"login": "lazywalker",
|
||||||
|
"name": "lazywalker",
|
||||||
|
"avatar_url": "https://avatars.githubusercontent.com/u/53956?v=4",
|
||||||
|
"profile": "https://github.com/lazywalker",
|
||||||
|
"contributions": [
|
||||||
|
"content"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"contributorsPerLine": 8,
|
"contributorsPerLine": 8,
|
||||||
|
|||||||
18
.github/workflows/lint.yml
vendored
Normal file
18
.github/workflows/lint.yml
vendored
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
name: Lint
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
pull_request:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
lint:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
- uses: DavidAnson/markdownlint-cli2-action@v9
|
||||||
|
with:
|
||||||
|
globs: "exercises/**/*.md"
|
||||||
4
.gitignore
vendored
4
.gitignore
vendored
@ -11,3 +11,7 @@ rust-project.json
|
|||||||
!.vscode/extensions.json
|
!.vscode/extensions.json
|
||||||
*.iml
|
*.iml
|
||||||
*.o
|
*.o
|
||||||
|
public/
|
||||||
|
|
||||||
|
# Local Netlify folder
|
||||||
|
.netlify
|
||||||
|
|||||||
2
.markdownlint.yml
Normal file
2
.markdownlint.yml
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
# MD013/line-length Line length, Expected: 80
|
||||||
|
MD013: false
|
||||||
19
AUTHORS.md
19
AUTHORS.md
@ -284,6 +284,25 @@ authors.
|
|||||||
<td align="center" valign="top" width="12.5%"><a href="http://keogami.ml"><img src="https://avatars.githubusercontent.com/u/41939011?v=4?s=100" width="100px;" alt="Keogami"/><br /><sub><b>Keogami</b></sub></a><br /><a href="https://github.com/rust-lang/rustlings/commits?author=keogami" title="Documentation">📖</a></td>
|
<td align="center" valign="top" width="12.5%"><a href="http://keogami.ml"><img src="https://avatars.githubusercontent.com/u/41939011?v=4?s=100" width="100px;" alt="Keogami"/><br /><sub><b>Keogami</b></sub></a><br /><a href="https://github.com/rust-lang/rustlings/commits?author=keogami" title="Documentation">📖</a></td>
|
||||||
<td align="center" valign="top" width="12.5%"><a href="https://github.com/ahresse"><img src="https://avatars.githubusercontent.com/u/28402488?v=4?s=100" width="100px;" alt="Alexandre Esse"/><br /><sub><b>Alexandre Esse</b></sub></a><br /><a href="#content-ahresse" title="Content">🖋</a></td>
|
<td align="center" valign="top" width="12.5%"><a href="https://github.com/ahresse"><img src="https://avatars.githubusercontent.com/u/28402488?v=4?s=100" width="100px;" alt="Alexandre Esse"/><br /><sub><b>Alexandre Esse</b></sub></a><br /><a href="#content-ahresse" title="Content">🖋</a></td>
|
||||||
<td align="center" valign="top" width="12.5%"><a href="https://resilient.tech"><img src="https://avatars.githubusercontent.com/u/16315650?v=4?s=100" width="100px;" alt="Sagar Vora"/><br /><sub><b>Sagar Vora</b></sub></a><br /><a href="#content-sagarvora" title="Content">🖋</a></td>
|
<td align="center" valign="top" width="12.5%"><a href="https://resilient.tech"><img src="https://avatars.githubusercontent.com/u/16315650?v=4?s=100" width="100px;" alt="Sagar Vora"/><br /><sub><b>Sagar Vora</b></sub></a><br /><a href="#content-sagarvora" title="Content">🖋</a></td>
|
||||||
|
<td align="center" valign="top" width="12.5%"><a href="https://github.com/poneciak57"><img src="https://avatars.githubusercontent.com/u/94321164?v=4?s=100" width="100px;" alt="Kacper Poneta"/><br /><sub><b>Kacper Poneta</b></sub></a><br /><a href="#content-poneciak57" title="Content">🖋</a></td>
|
||||||
|
<td align="center" valign="top" width="12.5%"><a href="https://ktheory.com/"><img src="https://avatars.githubusercontent.com/u/975?v=4?s=100" width="100px;" alt="Aaron Suggs"/><br /><sub><b>Aaron Suggs</b></sub></a><br /><a href="#content-ktheory" title="Content">🖋</a></td>
|
||||||
|
<td align="center" valign="top" width="12.5%"><a href="https://github.com/alexwh"><img src="https://avatars.githubusercontent.com/u/1723612?v=4?s=100" width="100px;" alt="Alex"/><br /><sub><b>Alex</b></sub></a><br /><a href="#content-alexwh" title="Content">🖋</a></td>
|
||||||
|
<td align="center" valign="top" width="12.5%"><a href="https://github.com/stornquist"><img src="https://avatars.githubusercontent.com/u/42915664?v=4?s=100" width="100px;" alt="Sebastian Törnquist"/><br /><sub><b>Sebastian Törnquist</b></sub></a><br /><a href="#content-stornquist" title="Content">🖋</a></td>
|
||||||
|
<td align="center" valign="top" width="12.5%"><a href="http://smlavine.com"><img src="https://avatars.githubusercontent.com/u/33563640?v=4?s=100" width="100px;" alt="Sebastian LaVine"/><br /><sub><b>Sebastian LaVine</b></sub></a><br /><a href="https://github.com/rust-lang/rustlings/commits?author=smlavine" title="Code">💻</a></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td align="center" valign="top" width="12.5%"><a href="http://www.alangerber.us"><img src="https://avatars.githubusercontent.com/u/201313?v=4?s=100" width="100px;" alt="Alan Gerber"/><br /><sub><b>Alan Gerber</b></sub></a><br /><a href="#content-akgerber" title="Content">🖋</a></td>
|
||||||
|
<td align="center" valign="top" width="12.5%"><a href="http://esotuvaka.github.io"><img src="https://avatars.githubusercontent.com/u/104941850?v=4?s=100" width="100px;" alt="Eric"/><br /><sub><b>Eric</b></sub></a><br /><a href="#content-esotuvaka" title="Content">🖋</a></td>
|
||||||
|
<td align="center" valign="top" width="12.5%"><a href="https://github.com/az0977776"><img src="https://avatars.githubusercontent.com/u/9172038?v=4?s=100" width="100px;" alt="Aaron Wang"/><br /><sub><b>Aaron Wang</b></sub></a><br /><a href="#content-az0977776" title="Content">🖋</a></td>
|
||||||
|
<td align="center" valign="top" width="12.5%"><a href="https://github.com/nmay231"><img src="https://avatars.githubusercontent.com/u/35386821?v=4?s=100" width="100px;" alt="Noah"/><br /><sub><b>Noah</b></sub></a><br /><a href="#content-nmay231" title="Content">🖋</a></td>
|
||||||
|
<td align="center" valign="top" width="12.5%"><a href="https://github.com/rb5014"><img src="https://avatars.githubusercontent.com/u/105397317?v=4?s=100" width="100px;" alt="rb5014"/><br /><sub><b>rb5014</b></sub></a><br /><a href="#content-rb5014" title="Content">🖋</a></td>
|
||||||
|
<td align="center" valign="top" width="12.5%"><a href="https://github.com/deedy5"><img src="https://avatars.githubusercontent.com/u/65482418?v=4?s=100" width="100px;" alt="deedy5"/><br /><sub><b>deedy5</b></sub></a><br /><a href="#content-deedy5" title="Content">🖋</a></td>
|
||||||
|
<td align="center" valign="top" width="12.5%"><a href="https://github.com/lionel-rowe"><img src="https://avatars.githubusercontent.com/u/26078826?v=4?s=100" width="100px;" alt="lionel-rowe"/><br /><sub><b>lionel-rowe</b></sub></a><br /><a href="#content-lionel-rowe" title="Content">🖋</a></td>
|
||||||
|
<td align="center" valign="top" width="12.5%"><a href="https://github.com/Ben2917"><img src="https://avatars.githubusercontent.com/u/10279994?v=4?s=100" width="100px;" alt="Ben"/><br /><sub><b>Ben</b></sub></a><br /><a href="#content-Ben2917" title="Content">🖋</a></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td align="center" valign="top" width="12.5%"><a href="https://github.com/b1ue64"><img src="https://avatars.githubusercontent.com/u/77976308?v=4?s=100" width="100px;" alt="b1ue64"/><br /><sub><b>b1ue64</b></sub></a><br /><a href="#content-b1ue64" title="Content">🖋</a></td>
|
||||||
|
<td align="center" valign="top" width="12.5%"><a href="https://github.com/lazywalker"><img src="https://avatars.githubusercontent.com/u/53956?v=4?s=100" width="100px;" alt="lazywalker"/><br /><sub><b>lazywalker</b></sub></a><br /><a href="#content-lazywalker" title="Content">🖋</a></td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
|||||||
42
CHANGELOG.md
42
CHANGELOG.md
@ -1,3 +1,45 @@
|
|||||||
|
<a name="5.5.1"></a>
|
||||||
|
## 5.5.1 (2023-05-17)
|
||||||
|
|
||||||
|
#### Fixed
|
||||||
|
|
||||||
|
- Reverted `rust-project.json` path generation due to an upstream `rust-analyzer` fix.
|
||||||
|
|
||||||
|
<a name="5.5.0"></a>
|
||||||
|
## 5.5.0 (2023-05-17)
|
||||||
|
|
||||||
|
#### Added
|
||||||
|
|
||||||
|
- `strings2`: Added a reference to the book chapter for reference conversion
|
||||||
|
- `lifetimes`: Added a link to the lifetimekata project
|
||||||
|
- Added a new `tests4` exercises, which teaches about testing for panics
|
||||||
|
- Added a `!` prefix command to watch mode that runs an external command
|
||||||
|
- Added a `--success-hints` option to watch mode that shows hints on exercise success
|
||||||
|
|
||||||
|
#### Changed
|
||||||
|
|
||||||
|
- `vecs2`: Renamed iterator variable bindings for clarify
|
||||||
|
- `lifetimes`: Changed order of book references
|
||||||
|
- `hashmaps2`: Clarified instructions in the todo block
|
||||||
|
- Moved lifetime exercises before test exercises (via the recommended book ordering)
|
||||||
|
- `options2`: Improved tests for layering options
|
||||||
|
- `modules2`: Added more information to the hint
|
||||||
|
|
||||||
|
#### Fixed
|
||||||
|
|
||||||
|
- `errors2`: Corrected a comment wording
|
||||||
|
- `iterators2`: Fixed a spelling mistake in the hint text
|
||||||
|
- `variables`: Wrapped the mut keyword with backticks for readability
|
||||||
|
- `move_semantics2`: Removed references to line numbers
|
||||||
|
- `cow1`: Clarified the `owned_no_mutation` comments
|
||||||
|
- `options3`: Changed exercise to panic when no match is found
|
||||||
|
- `rustlings lsp` now generates absolute paths, which should fix VSCode `rust-analyzer` usage on Windows
|
||||||
|
|
||||||
|
#### Housekeeping
|
||||||
|
|
||||||
|
- Added a markdown linter to run on GitHub actions
|
||||||
|
- Split quick installation section into two code blocks
|
||||||
|
|
||||||
<a name="5.4.1"></a>
|
<a name="5.4.1"></a>
|
||||||
## 5.4.1 (2023-03-10)
|
## 5.4.1 (2023-03-10)
|
||||||
|
|
||||||
|
|||||||
2
Cargo.lock
generated
2
Cargo.lock
generated
@ -441,7 +441,7 @@ checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustlings"
|
name = "rustlings"
|
||||||
version = "5.4.1"
|
version = "5.5.1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"argh",
|
"argh",
|
||||||
"assert_cmd",
|
"assert_cmd",
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "rustlings"
|
name = "rustlings"
|
||||||
version = "5.4.1"
|
description = "Small exercises to get you used to reading and writing Rust code!"
|
||||||
|
version = "5.5.1"
|
||||||
authors = [
|
authors = [
|
||||||
"Liv <mokou@fastmail.com>",
|
"Liv <mokou@fastmail.com>",
|
||||||
"Carol (Nichols || Goulding) <carol.nichols@gmail.com>",
|
"Carol (Nichols || Goulding) <carol.nichols@gmail.com>",
|
||||||
|
|||||||
12
README.md
12
README.md
@ -1,5 +1,9 @@
|
|||||||
|
<div class="oranda-hide">
|
||||||
|
|
||||||
# rustlings 🦀❤️
|
# rustlings 🦀❤️
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
Greetings and welcome to `rustlings`. This project contains small exercises to get you used to reading and writing Rust code. This includes reading and responding to compiler messages!
|
Greetings and welcome to `rustlings`. This project contains small exercises to get you used to reading and writing Rust code. This includes reading and responding to compiler messages!
|
||||||
|
|
||||||
_...looking for the old, web-based version of Rustlings? Try [here](https://github.com/rust-lang/rustlings/tree/rustlings-1)_
|
_...looking for the old, web-based version of Rustlings? Try [here](https://github.com/rust-lang/rustlings/tree/rustlings-1)_
|
||||||
@ -36,8 +40,8 @@ This will install Rustlings and give you access to the `rustlings` command. Run
|
|||||||
Basically: Clone the repository at the latest tag, finally run `nix develop` or `nix-shell`.
|
Basically: Clone the repository at the latest tag, finally run `nix develop` or `nix-shell`.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# find out the latest version at https://github.com/rust-lang/rustlings/releases/latest (on edit 5.4.1)
|
# find out the latest version at https://github.com/rust-lang/rustlings/releases/latest (on edit 5.5.1)
|
||||||
git clone -b 5.4.1 --depth 1 https://github.com/rust-lang/rustlings
|
git clone -b 5.5.1 --depth 1 https://github.com/rust-lang/rustlings
|
||||||
cd rustlings
|
cd rustlings
|
||||||
# if nix version > 2.3
|
# if nix version > 2.3
|
||||||
nix develop
|
nix develop
|
||||||
@ -74,8 +78,8 @@ If you get a permission denied message, you might have to exclude the directory
|
|||||||
Basically: Clone the repository at the latest tag, run `cargo install --path .`.
|
Basically: Clone the repository at the latest tag, run `cargo install --path .`.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# find out the latest version at https://github.com/rust-lang/rustlings/releases/latest (on edit 5.4.1)
|
# find out the latest version at https://github.com/rust-lang/rustlings/releases/latest (on edit 5.5.1)
|
||||||
git clone -b 5.4.1 --depth 1 https://github.com/rust-lang/rustlings
|
git clone -b 5.5.1 --depth 1 https://github.com/rust-lang/rustlings
|
||||||
cd rustlings
|
cd rustlings
|
||||||
cargo install --force --path .
|
cargo install --force --path .
|
||||||
```
|
```
|
||||||
|
|||||||
@ -6,6 +6,7 @@ The simplest form of type conversion is a type cast expression. It is denoted wi
|
|||||||
|
|
||||||
Rust also offers traits that facilitate type conversions upon implementation. These traits can be found under the [`convert`](https://doc.rust-lang.org/std/convert/index.html) module.
|
Rust also offers traits that facilitate type conversions upon implementation. These traits can be found under the [`convert`](https://doc.rust-lang.org/std/convert/index.html) module.
|
||||||
The traits are the following:
|
The traits are the following:
|
||||||
|
|
||||||
- `From` and `Into` covered in [`from_into`](from_into.rs)
|
- `From` and `Into` covered in [`from_into`](from_into.rs)
|
||||||
- `TryFrom` and `TryInto` covered in [`try_from_into`](try_from_into.rs)
|
- `TryFrom` and `TryInto` covered in [`try_from_into`](try_from_into.rs)
|
||||||
- `AsRef` and `AsMut` covered in [`as_ref_mut`](as_ref_mut.rs)
|
- `AsRef` and `AsMut` covered in [`as_ref_mut`](as_ref_mut.rs)
|
||||||
@ -17,5 +18,6 @@ These should be the main ways ***within the standard library*** to convert data
|
|||||||
## Further information
|
## Further information
|
||||||
|
|
||||||
These are not directly covered in the book, but the standard library has a great documentation for it.
|
These are not directly covered in the book, but the standard library has a great documentation for it.
|
||||||
|
|
||||||
- [conversions](https://doc.rust-lang.org/std/convert/index.html)
|
- [conversions](https://doc.rust-lang.org/std/convert/index.html)
|
||||||
- [`FromStr` trait](https://doc.rust-lang.org/std/str/trait.FromStr.html)
|
- [`FromStr` trait](https://doc.rust-lang.org/std/str/trait.FromStr.html)
|
||||||
@ -1,4 +1,5 @@
|
|||||||
# Hashmaps
|
# Hashmaps
|
||||||
|
|
||||||
A *hash map* allows you to associate a value with a particular key.
|
A *hash map* allows you to associate a value with a particular key.
|
||||||
You may also know this by the names [*unordered map* in C++](https://en.cppreference.com/w/cpp/container/unordered_map),
|
You may also know this by the names [*unordered map* in C++](https://en.cppreference.com/w/cpp/container/unordered_map),
|
||||||
[*dictionary* in Python](https://docs.python.org/3/tutorial/datastructures.html#dictionaries) or an *associative array* in other languages.
|
[*dictionary* in Python](https://docs.python.org/3/tutorial/datastructures.html#dictionaries) or an *associative array* in other languages.
|
||||||
|
|||||||
@ -2,13 +2,11 @@
|
|||||||
// Let's define a simple model to track Rustlings exercise progress. Progress
|
// Let's define a simple model to track Rustlings exercise progress. Progress
|
||||||
// will be modelled using a hash map. The name of the exercise is the key and
|
// will be modelled using a hash map. The name of the exercise is the key and
|
||||||
// the progress is the value. Two counting functions were created to count the
|
// the progress is the value. Two counting functions were created to count the
|
||||||
// number of exercises with a given progress. These counting functions use
|
// number of exercises with a given progress. Recreate this counting
|
||||||
// imperative style for loops. Recreate this counting functionality using
|
// functionality using iterators. Try not to use imperative loops (for, while).
|
||||||
// iterators. Only the two iterator methods (count_iterator and
|
// Only the two iterator methods (count_iterator and count_collection_iterator)
|
||||||
// count_collection_iterator) need to be modified.
|
// need to be modified.
|
||||||
// Execute `rustlings hint iterators5` or use the `hint` watch subcommand for a hint.
|
// Execute `rustlings hint iterators5` or use the `hint` watch subcommand for a hint.
|
||||||
//
|
|
||||||
// Make the code compile and the tests pass.
|
|
||||||
|
|
||||||
// I AM NOT DONE
|
// I AM NOT DONE
|
||||||
|
|
||||||
@ -67,12 +65,27 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn count_equals_for() {
|
fn count_some() {
|
||||||
let map = get_map();
|
let map = get_map();
|
||||||
assert_eq!(
|
assert_eq!(1, count_iterator(&map, Progress::Some));
|
||||||
count_for(&map, Progress::Complete),
|
}
|
||||||
count_iterator(&map, Progress::Complete)
|
|
||||||
);
|
#[test]
|
||||||
|
fn count_none() {
|
||||||
|
let map = get_map();
|
||||||
|
assert_eq!(2, count_iterator(&map, Progress::None));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn count_complete_equals_for() {
|
||||||
|
let map = get_map();
|
||||||
|
let progress_states = vec![Progress::Complete, Progress::Some, Progress::None];
|
||||||
|
for progress_state in progress_states {
|
||||||
|
assert_eq!(
|
||||||
|
count_for(&map, progress_state),
|
||||||
|
count_iterator(&map, progress_state)
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -85,12 +98,28 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn count_collection_equals_for() {
|
fn count_collection_some() {
|
||||||
let collection = get_vec_map();
|
let collection = get_vec_map();
|
||||||
assert_eq!(
|
assert_eq!(1, count_collection_iterator(&collection, Progress::Some));
|
||||||
count_collection_for(&collection, Progress::Complete),
|
}
|
||||||
count_collection_iterator(&collection, Progress::Complete)
|
|
||||||
);
|
#[test]
|
||||||
|
fn count_collection_none() {
|
||||||
|
let collection = get_vec_map();
|
||||||
|
assert_eq!(4, count_collection_iterator(&collection, Progress::None));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn count_collection_equals_for() {
|
||||||
|
let progress_states = vec![Progress::Complete, Progress::Some, Progress::None];
|
||||||
|
let collection = get_vec_map();
|
||||||
|
|
||||||
|
for progress_state in progress_states {
|
||||||
|
assert_eq!(
|
||||||
|
count_collection_for(&collection, progress_state),
|
||||||
|
count_collection_iterator(&collection, progress_state)
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_map() -> HashMap<String, Progress> {
|
fn get_map() -> HashMap<String, Progress> {
|
||||||
|
|||||||
@ -1,12 +1,16 @@
|
|||||||
// move_semantics2.rs
|
// move_semantics2.rs
|
||||||
// Make me compile without changing line 13 or moving line 10!
|
|
||||||
// Execute `rustlings hint move_semantics2` or use the `hint` watch subcommand for a hint.
|
// Execute `rustlings hint move_semantics2` or use the `hint` watch subcommand for a hint.
|
||||||
|
|
||||||
|
// Expected output:
|
||||||
|
// vec0 has length 3 content `[22, 44, 66]`
|
||||||
|
// vec1 has length 4 content `[22, 44, 66, 88]`
|
||||||
|
|
||||||
// I AM NOT DONE
|
// I AM NOT DONE
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let vec0 = Vec::new();
|
let vec0 = Vec::new();
|
||||||
|
|
||||||
|
// Do not move the following line!
|
||||||
let mut vec1 = fill_vec(vec0);
|
let mut vec1 = fill_vec(vec0);
|
||||||
|
|
||||||
// Do not change the following line!
|
// Do not change the following line!
|
||||||
|
|||||||
@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
Type Option represents an optional value: every Option is either Some and contains a value, or None, and does not.
|
Type Option represents an optional value: every Option is either Some and contains a value, or None, and does not.
|
||||||
Option types are very common in Rust code, as they have a number of uses:
|
Option types are very common in Rust code, as they have a number of uses:
|
||||||
|
|
||||||
- Initial values
|
- Initial values
|
||||||
- Return values for functions that are not defined over their entire input range (partial functions)
|
- Return values for functions that are not defined over their entire input range (partial functions)
|
||||||
- Return value for otherwise reporting simple errors, where None is returned on error
|
- Return value for otherwise reporting simple errors, where None is returned on error
|
||||||
|
|||||||
@ -18,17 +18,22 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn layered_option() {
|
fn layered_option() {
|
||||||
let mut range = 10;
|
let range = 10;
|
||||||
let mut optional_integers: Vec<Option<i8>> = Vec::new();
|
let mut optional_integers: Vec<Option<i8>> = vec![None];
|
||||||
for i in 0..(range + 1) {
|
|
||||||
|
for i in 1..(range + 1) {
|
||||||
optional_integers.push(Some(i));
|
optional_integers.push(Some(i));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let mut cursor = range;
|
||||||
|
|
||||||
// TODO: make this a while let statement - remember that vector.pop also adds another layer of Option<T>
|
// TODO: make this a while let statement - remember that vector.pop also adds another layer of Option<T>
|
||||||
// You can stack `Option<T>`'s into while let and if let
|
// You can stack `Option<T>`s into while let and if let
|
||||||
integer = optional_integers.pop() {
|
integer = optional_integers.pop() {
|
||||||
assert_eq!(integer, range);
|
assert_eq!(integer, cursor);
|
||||||
range -= 1;
|
cursor -= 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
assert_eq!(cursor, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -13,7 +13,7 @@ fn main() {
|
|||||||
|
|
||||||
match y {
|
match y {
|
||||||
Some(p) => println!("Co-ordinates are {},{} ", p.x, p.y),
|
Some(p) => println!("Co-ordinates are {},{} ", p.x, p.y),
|
||||||
_ => println!("no match"),
|
_ => panic!("no match!"),
|
||||||
}
|
}
|
||||||
y; // Fix without deleting this line.
|
y; // Fix without deleting this line.
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
# Smart Pointers
|
# Smart Pointers
|
||||||
|
|
||||||
In Rust, smart pointers are variables that contain an address in memory and reference some other data, but they also have additional metadata and capabilities.
|
In Rust, smart pointers are variables that contain an address in memory and reference some other data, but they also have additional metadata and capabilities.
|
||||||
Smart pointers in Rust often own the data they point to, while references only borrow data.
|
Smart pointers in Rust often own the data they point to, while references only borrow data.
|
||||||
|
|
||||||
|
|||||||
@ -52,7 +52,8 @@ mod tests {
|
|||||||
fn owned_no_mutation() -> Result<(), &'static str> {
|
fn owned_no_mutation() -> Result<(), &'static str> {
|
||||||
// We can also pass `slice` without `&` so Cow owns it directly.
|
// We can also pass `slice` without `&` so Cow owns it directly.
|
||||||
// In this case no mutation occurs and thus also no clone,
|
// In this case no mutation occurs and thus also no clone,
|
||||||
// but the result is still owned because it always was.
|
// but the result is still owned because it was never borrowed
|
||||||
|
// or mutated.
|
||||||
let slice = vec![0, 1, 2];
|
let slice = vec![0, 1, 2];
|
||||||
let mut input = Cow::from(slice);
|
let mut input = Cow::from(slice);
|
||||||
match abs_all(&mut input) {
|
match abs_all(&mut input) {
|
||||||
|
|||||||
45
exercises/tests/tests4.rs
Normal file
45
exercises/tests/tests4.rs
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
// tests4.rs
|
||||||
|
// Make sure that we're testing for the correct conditions!
|
||||||
|
// Execute `rustlings hint tests4` or use the `hint` watch subcommand for a hint.
|
||||||
|
|
||||||
|
// I AM NOT DONE
|
||||||
|
|
||||||
|
struct Rectangle {
|
||||||
|
width: i32,
|
||||||
|
height: i32
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Rectangle {
|
||||||
|
// Only change the test functions themselves
|
||||||
|
pub fn new(width: i32, height: i32) -> Self {
|
||||||
|
if width <= 0 || height <= 0 {
|
||||||
|
panic!("Rectangle width and height cannot be negative!")
|
||||||
|
}
|
||||||
|
Rectangle {width, height}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn correct_width_and_height() {
|
||||||
|
// This test should check if the rectangle is the size that we pass into its constructor
|
||||||
|
let rect = Rectangle::new(10, 20);
|
||||||
|
assert_eq!(???, 10); // check width
|
||||||
|
assert_eq!(???, 20); // check height
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn negative_width() {
|
||||||
|
// This test should check if program panics when we try to create rectangle with negative width
|
||||||
|
let _rect = Rectangle::new(-10, 10);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn negative_height() {
|
||||||
|
// This test should check if program panics when we try to create rectangle with negative height
|
||||||
|
let _rect = Rectangle::new(10, -10);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -7,13 +7,13 @@ Data types can implement traits. To do so, the methods making up the trait are d
|
|||||||
In this way, traits are somewhat similar to Java interfaces and C++ abstract classes.
|
In this way, traits are somewhat similar to Java interfaces and C++ abstract classes.
|
||||||
|
|
||||||
Some additional common Rust traits include:
|
Some additional common Rust traits include:
|
||||||
|
|
||||||
- `Clone` (the `clone` method)
|
- `Clone` (the `clone` method)
|
||||||
- `Display` (which allows formatted display via `{}`)
|
- `Display` (which allows formatted display via `{}`)
|
||||||
- `Debug` (which allows formatted display via `{:?}`)
|
- `Debug` (which allows formatted display via `{:?}`)
|
||||||
|
|
||||||
Because traits indicate shared behavior between data types, they are useful when writing generics.
|
Because traits indicate shared behavior between data types, they are useful when writing generics.
|
||||||
|
|
||||||
|
|
||||||
## Further information
|
## Further information
|
||||||
|
|
||||||
- [Traits](https://doc.rust-lang.org/book/ch10-02-traits.html)
|
- [Traits](https://doc.rust-lang.org/book/ch10-02-traits.html)
|
||||||
|
|||||||
@ -22,7 +22,7 @@
|
|||||||
rustlings =
|
rustlings =
|
||||||
pkgs.rustPlatform.buildRustPackage {
|
pkgs.rustPlatform.buildRustPackage {
|
||||||
name = "rustlings";
|
name = "rustlings";
|
||||||
version = "5.4.1";
|
version = "5.5.1";
|
||||||
|
|
||||||
buildInputs = cargoBuildInputs;
|
buildInputs = cargoBuildInputs;
|
||||||
|
|
||||||
|
|||||||
20
info.toml
20
info.toml
@ -325,8 +325,7 @@ doing one step and then fixing the compiler errors that result!
|
|||||||
So the end goal is to:
|
So the end goal is to:
|
||||||
- get rid of the first line in main that creates the new vector
|
- get rid of the first line in main that creates the new vector
|
||||||
- so then `vec0` doesn't exist, so we can't pass it to `fill_vec`
|
- so then `vec0` doesn't exist, so we can't pass it to `fill_vec`
|
||||||
- we don't want to pass anything to `fill_vec`, so its signature should
|
- `fill_vec` has had its signature changed, which our call should reflect
|
||||||
reflect that it does not take any arguments
|
|
||||||
- since we're not creating a new vec in `main` anymore, we need to create
|
- since we're not creating a new vec in `main` anymore, we need to create
|
||||||
a new vec in `fill_vec`, similarly to the way we did in `main`"""
|
a new vec in `fill_vec`, similarly to the way we did in `main`"""
|
||||||
|
|
||||||
@ -479,7 +478,8 @@ hint = """
|
|||||||
The delicious_snacks module is trying to present an external interface that is
|
The delicious_snacks module is trying to present an external interface that is
|
||||||
different than its internal structure (the `fruits` and `veggies` modules and
|
different than its internal structure (the `fruits` and `veggies` modules and
|
||||||
associated constants). Complete the `use` statements to fit the uses in main and
|
associated constants). Complete the `use` statements to fit the uses in main and
|
||||||
find the one keyword missing for both constants."""
|
find the one keyword missing for both constants.
|
||||||
|
Learn more at https://doc.rust-lang.org/book/ch07-04-bringing-paths-into-scope-with-the-use-keyword.html#re-exporting-names-with-pub-use"""
|
||||||
|
|
||||||
[[exercises]]
|
[[exercises]]
|
||||||
name = "modules3"
|
name = "modules3"
|
||||||
@ -807,6 +807,17 @@ You can call a function right where you're passing arguments to `assert!` -- so
|
|||||||
something like `assert!(having_fun())`. If you want to check that you indeed get false, you
|
something like `assert!(having_fun())`. If you want to check that you indeed get false, you
|
||||||
can negate the result of what you're doing using `!`, like `assert!(!having_fun())`."""
|
can negate the result of what you're doing using `!`, like `assert!(!having_fun())`."""
|
||||||
|
|
||||||
|
[[exercises]]
|
||||||
|
name = "tests4"
|
||||||
|
path = "exercises/tests/tests4.rs"
|
||||||
|
mode = "test"
|
||||||
|
hint = """
|
||||||
|
We expect method `Rectangle::new()` to panic for negative values.
|
||||||
|
To handle that you need to add a special attribute to the test function.
|
||||||
|
You can refer to the docs:
|
||||||
|
https://doc.rust-lang.org/stable/book/ch11-01-writing-tests.html#checking-for-panics-with-should_panic"""
|
||||||
|
|
||||||
|
|
||||||
# STANDARD LIBRARY TYPES
|
# STANDARD LIBRARY TYPES
|
||||||
|
|
||||||
[[exercises]]
|
[[exercises]]
|
||||||
@ -886,9 +897,6 @@ hint = """
|
|||||||
The documentation for the std::iter::Iterator trait contains numerous methods
|
The documentation for the std::iter::Iterator trait contains numerous methods
|
||||||
that would be helpful here.
|
that would be helpful here.
|
||||||
|
|
||||||
Return 0 from count_collection_iterator to make the code compile in order to
|
|
||||||
test count_iterator.
|
|
||||||
|
|
||||||
The collection variable in count_collection_iterator is a slice of HashMaps. It
|
The collection variable in count_collection_iterator is a slice of HashMaps. It
|
||||||
needs to be converted into an iterator in order to use the iterator methods.
|
needs to be converted into an iterator in order to use the iterator methods.
|
||||||
|
|
||||||
|
|||||||
10
oranda.json
Normal file
10
oranda.json
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
{
|
||||||
|
"homepage": "https://rustlings.cool",
|
||||||
|
"repository": "https://github.com/rust-lang/rustlings",
|
||||||
|
"analytics": {
|
||||||
|
"plausible": {
|
||||||
|
"domain": "rustlings.cool"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"changelog": false
|
||||||
|
}
|
||||||
48
src/main.rs
48
src/main.rs
@ -26,7 +26,7 @@ mod run;
|
|||||||
mod verify;
|
mod verify;
|
||||||
|
|
||||||
// In sync with crate version
|
// In sync with crate version
|
||||||
const VERSION: &str = "5.4.1";
|
const VERSION: &str = "5.5.1";
|
||||||
|
|
||||||
#[derive(FromArgs, PartialEq, Debug)]
|
#[derive(FromArgs, PartialEq, Debug)]
|
||||||
/// Rustlings is a collection of small exercises to get you used to writing and reading Rust code
|
/// Rustlings is a collection of small exercises to get you used to writing and reading Rust code
|
||||||
@ -61,7 +61,11 @@ struct VerifyArgs {}
|
|||||||
#[derive(FromArgs, PartialEq, Debug)]
|
#[derive(FromArgs, PartialEq, Debug)]
|
||||||
#[argh(subcommand, name = "watch")]
|
#[argh(subcommand, name = "watch")]
|
||||||
/// Reruns `verify` when files were edited
|
/// Reruns `verify` when files were edited
|
||||||
struct WatchArgs {}
|
struct WatchArgs {
|
||||||
|
/// show hints on success
|
||||||
|
#[argh(switch)]
|
||||||
|
success_hints: bool,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(FromArgs, PartialEq, Debug)]
|
#[derive(FromArgs, PartialEq, Debug)]
|
||||||
#[argh(subcommand, name = "run")]
|
#[argh(subcommand, name = "run")]
|
||||||
@ -229,7 +233,7 @@ fn main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Subcommands::Verify(_subargs) => {
|
Subcommands::Verify(_subargs) => {
|
||||||
verify(&exercises, (0, exercises.len()), verbose)
|
verify(&exercises, (0, exercises.len()), verbose, false)
|
||||||
.unwrap_or_else(|_| std::process::exit(1));
|
.unwrap_or_else(|_| std::process::exit(1));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -252,7 +256,7 @@ fn main() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Subcommands::Watch(_subargs) => match watch(&exercises, verbose) {
|
Subcommands::Watch(_subargs) => match watch(&exercises, verbose, _subargs.success_hints) {
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
println!(
|
println!(
|
||||||
"Error: Could not watch your progress. Error message was {:?}.",
|
"Error: Could not watch your progress. Error message was {:?}.",
|
||||||
@ -298,13 +302,21 @@ fn spawn_watch_shell(
|
|||||||
println!("Bye!");
|
println!("Bye!");
|
||||||
} else if input.eq("help") {
|
} else if input.eq("help") {
|
||||||
println!("Commands available to you in watch mode:");
|
println!("Commands available to you in watch mode:");
|
||||||
println!(" hint - prints the current exercise's hint");
|
println!(" hint - prints the current exercise's hint");
|
||||||
println!(" clear - clears the screen");
|
println!(" clear - clears the screen");
|
||||||
println!(" quit - quits watch mode");
|
println!(" quit - quits watch mode");
|
||||||
println!(" help - displays this help message");
|
println!(" !<cmd> - executes a command, like `!rustc --explain E0381`");
|
||||||
|
println!(" help - displays this help message");
|
||||||
println!();
|
println!();
|
||||||
println!("Watch mode automatically re-evaluates the current exercise");
|
println!("Watch mode automatically re-evaluates the current exercise");
|
||||||
println!("when you edit a file's contents.")
|
println!("when you edit a file's contents.")
|
||||||
|
} else if let Some(cmd) = input.strip_prefix('!') {
|
||||||
|
let parts: Vec<&str> = cmd.split_whitespace().collect();
|
||||||
|
if parts.is_empty() {
|
||||||
|
println!("no command provided");
|
||||||
|
} else if let Err(e) = Command::new(parts[0]).args(&parts[1..]).status() {
|
||||||
|
println!("failed to execute command `{}`: {}", cmd, e);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
println!("unknown command: {input}");
|
println!("unknown command: {input}");
|
||||||
}
|
}
|
||||||
@ -340,7 +352,11 @@ enum WatchStatus {
|
|||||||
Unfinished,
|
Unfinished,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn watch(exercises: &[Exercise], verbose: bool) -> notify::Result<WatchStatus> {
|
fn watch(
|
||||||
|
exercises: &[Exercise],
|
||||||
|
verbose: bool,
|
||||||
|
success_hints: bool,
|
||||||
|
) -> notify::Result<WatchStatus> {
|
||||||
/* Clears the terminal with an ANSI escape code.
|
/* Clears the terminal with an ANSI escape code.
|
||||||
Works in UNIX and newer Windows terminals. */
|
Works in UNIX and newer Windows terminals. */
|
||||||
fn clear_screen() {
|
fn clear_screen() {
|
||||||
@ -356,7 +372,12 @@ fn watch(exercises: &[Exercise], verbose: bool) -> notify::Result<WatchStatus> {
|
|||||||
clear_screen();
|
clear_screen();
|
||||||
|
|
||||||
let to_owned_hint = |t: &Exercise| t.hint.to_owned();
|
let to_owned_hint = |t: &Exercise| t.hint.to_owned();
|
||||||
let failed_exercise_hint = match verify(exercises.iter(), (0, exercises.len()), verbose) {
|
let failed_exercise_hint = match verify(
|
||||||
|
exercises.iter(),
|
||||||
|
(0, exercises.len()),
|
||||||
|
verbose,
|
||||||
|
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(to_owned_hint(exercise)))),
|
||||||
};
|
};
|
||||||
@ -378,7 +399,12 @@ fn watch(exercises: &[Exercise], verbose: bool) -> notify::Result<WatchStatus> {
|
|||||||
);
|
);
|
||||||
let num_done = exercises.iter().filter(|e| e.looks_done()).count();
|
let num_done = exercises.iter().filter(|e| e.looks_done()).count();
|
||||||
clear_screen();
|
clear_screen();
|
||||||
match verify(pending_exercises, (num_done, exercises.len()), verbose) {
|
match verify(
|
||||||
|
pending_exercises,
|
||||||
|
(num_done, exercises.len()),
|
||||||
|
verbose,
|
||||||
|
success_hints,
|
||||||
|
) {
|
||||||
Ok(_) => return Ok(WatchStatus::Finished),
|
Ok(_) => return Ok(WatchStatus::Finished),
|
||||||
Err(exercise) => {
|
Err(exercise) => {
|
||||||
let mut failed_exercise_hint = failed_exercise_hint.lock().unwrap();
|
let mut failed_exercise_hint = failed_exercise_hint.lock().unwrap();
|
||||||
|
|||||||
@ -2,6 +2,7 @@ use glob::glob;
|
|||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::env;
|
use std::env;
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
|
use std::path::PathBuf;
|
||||||
use std::process::Command;
|
use std::process::Command;
|
||||||
|
|
||||||
/// Contains the structure of resulting rust-project.json file
|
/// Contains the structure of resulting rust-project.json file
|
||||||
@ -38,11 +39,11 @@ impl RustAnalyzerProject {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// If path contains .rs extension, add a crate to `rust-project.json`
|
/// If path contains .rs extension, add a crate to `rust-project.json`
|
||||||
fn path_to_json(&mut self, path: String) {
|
fn path_to_json(&mut self, path: PathBuf) -> Result<(), Box<dyn Error>> {
|
||||||
if let Some((_, ext)) = path.split_once('.') {
|
if let Some(ext) = path.extension() {
|
||||||
if ext == "rs" {
|
if ext == "rs" {
|
||||||
self.crates.push(Crate {
|
self.crates.push(Crate {
|
||||||
root_module: path,
|
root_module: path.display().to_string(),
|
||||||
edition: "2021".to_string(),
|
edition: "2021".to_string(),
|
||||||
deps: Vec::new(),
|
deps: Vec::new(),
|
||||||
// This allows rust_analyzer to work inside #[test] blocks
|
// This allows rust_analyzer to work inside #[test] blocks
|
||||||
@ -50,15 +51,16 @@ impl RustAnalyzerProject {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parse the exercises folder for .rs files, any matches will create
|
/// Parse the exercises folder for .rs files, any matches will create
|
||||||
/// a new `crate` in rust-project.json which allows rust-analyzer to
|
/// a new `crate` in rust-project.json which allows rust-analyzer to
|
||||||
/// treat it like a normal binary
|
/// treat it like a normal binary
|
||||||
pub fn exercises_to_json(&mut self) -> Result<(), Box<dyn Error>> {
|
pub fn exercises_to_json(&mut self) -> Result<(), Box<dyn Error>> {
|
||||||
for e in glob("./exercises/**/*")? {
|
for path in glob("./exercises/**/*")? {
|
||||||
let path = e?.to_string_lossy().to_string();
|
self.path_to_json(path?)?;
|
||||||
self.path_to_json(path);
|
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
@ -12,6 +12,7 @@ pub fn verify<'a>(
|
|||||||
exercises: impl IntoIterator<Item = &'a Exercise>,
|
exercises: impl IntoIterator<Item = &'a Exercise>,
|
||||||
progress: (usize, usize),
|
progress: (usize, usize),
|
||||||
verbose: bool,
|
verbose: bool,
|
||||||
|
success_hints: bool,
|
||||||
) -> Result<(), &'a Exercise> {
|
) -> Result<(), &'a Exercise> {
|
||||||
let (num_done, total) = progress;
|
let (num_done, total) = progress;
|
||||||
let bar = ProgressBar::new(total as u64);
|
let bar = ProgressBar::new(total as u64);
|
||||||
@ -25,9 +26,9 @@ pub fn verify<'a>(
|
|||||||
|
|
||||||
for exercise in exercises {
|
for exercise in exercises {
|
||||||
let compile_result = match exercise.mode {
|
let compile_result = match exercise.mode {
|
||||||
Mode::Test => compile_and_test(exercise, RunMode::Interactive, verbose),
|
Mode::Test => compile_and_test(exercise, RunMode::Interactive, verbose, success_hints),
|
||||||
Mode::Compile => compile_and_run_interactively(exercise),
|
Mode::Compile => compile_and_run_interactively(exercise, success_hints),
|
||||||
Mode::Clippy => compile_only(exercise),
|
Mode::Clippy => compile_only(exercise, success_hints),
|
||||||
};
|
};
|
||||||
if !compile_result.unwrap_or(false) {
|
if !compile_result.unwrap_or(false) {
|
||||||
return Err(exercise);
|
return Err(exercise);
|
||||||
@ -46,12 +47,12 @@ enum RunMode {
|
|||||||
|
|
||||||
// Compile and run the resulting test harness of the given Exercise
|
// Compile and run the resulting test harness of the given Exercise
|
||||||
pub fn test(exercise: &Exercise, verbose: bool) -> Result<(), ()> {
|
pub fn test(exercise: &Exercise, verbose: bool) -> Result<(), ()> {
|
||||||
compile_and_test(exercise, RunMode::NonInteractive, verbose)?;
|
compile_and_test(exercise, RunMode::NonInteractive, verbose, false)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
// Invoke the rust compiler without running the resulting binary
|
// Invoke the rust compiler without running the resulting binary
|
||||||
fn compile_only(exercise: &Exercise) -> Result<bool, ()> {
|
fn compile_only(exercise: &Exercise, success_hints: bool) -> Result<bool, ()> {
|
||||||
let progress_bar = ProgressBar::new_spinner();
|
let progress_bar = ProgressBar::new_spinner();
|
||||||
progress_bar.set_message(format!("Compiling {exercise}..."));
|
progress_bar.set_message(format!("Compiling {exercise}..."));
|
||||||
progress_bar.enable_steady_tick(100);
|
progress_bar.enable_steady_tick(100);
|
||||||
@ -59,11 +60,11 @@ fn compile_only(exercise: &Exercise) -> Result<bool, ()> {
|
|||||||
let _ = compile(exercise, &progress_bar)?;
|
let _ = compile(exercise, &progress_bar)?;
|
||||||
progress_bar.finish_and_clear();
|
progress_bar.finish_and_clear();
|
||||||
|
|
||||||
Ok(prompt_for_completion(exercise, None))
|
Ok(prompt_for_completion(exercise, None, success_hints))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Compile the given Exercise and run the resulting binary in an interactive mode
|
// Compile the given Exercise and run the resulting binary in an interactive mode
|
||||||
fn compile_and_run_interactively(exercise: &Exercise) -> Result<bool, ()> {
|
fn compile_and_run_interactively(exercise: &Exercise, success_hints: bool) -> Result<bool, ()> {
|
||||||
let progress_bar = ProgressBar::new_spinner();
|
let progress_bar = ProgressBar::new_spinner();
|
||||||
progress_bar.set_message(format!("Compiling {exercise}..."));
|
progress_bar.set_message(format!("Compiling {exercise}..."));
|
||||||
progress_bar.enable_steady_tick(100);
|
progress_bar.enable_steady_tick(100);
|
||||||
@ -84,12 +85,12 @@ fn compile_and_run_interactively(exercise: &Exercise) -> Result<bool, ()> {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(prompt_for_completion(exercise, Some(output.stdout)))
|
Ok(prompt_for_completion(exercise, Some(output.stdout), success_hints))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Compile the given Exercise as a test harness and display
|
// Compile the given Exercise as a test harness and display
|
||||||
// the output if verbose is set to true
|
// the output if verbose is set to true
|
||||||
fn compile_and_test(exercise: &Exercise, run_mode: RunMode, verbose: bool) -> Result<bool, ()> {
|
fn compile_and_test(exercise: &Exercise, run_mode: RunMode, verbose: bool, success_hints: bool) -> Result<bool, ()> {
|
||||||
let progress_bar = ProgressBar::new_spinner();
|
let progress_bar = ProgressBar::new_spinner();
|
||||||
progress_bar.set_message(format!("Testing {exercise}..."));
|
progress_bar.set_message(format!("Testing {exercise}..."));
|
||||||
progress_bar.enable_steady_tick(100);
|
progress_bar.enable_steady_tick(100);
|
||||||
@ -104,7 +105,7 @@ fn compile_and_test(exercise: &Exercise, run_mode: RunMode, verbose: bool) -> Re
|
|||||||
println!("{}", output.stdout);
|
println!("{}", output.stdout);
|
||||||
}
|
}
|
||||||
if let RunMode::Interactive = run_mode {
|
if let RunMode::Interactive = run_mode {
|
||||||
Ok(prompt_for_completion(exercise, None))
|
Ok(prompt_for_completion(exercise, None, success_hints))
|
||||||
} else {
|
} else {
|
||||||
Ok(true)
|
Ok(true)
|
||||||
}
|
}
|
||||||
@ -142,12 +143,11 @@ fn compile<'a, 'b>(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn prompt_for_completion(exercise: &Exercise, prompt_output: Option<String>) -> bool {
|
fn prompt_for_completion(exercise: &Exercise, prompt_output: Option<String>, success_hints: bool) -> bool {
|
||||||
let context = match exercise.state() {
|
let context = match exercise.state() {
|
||||||
State::Done => return true,
|
State::Done => return true,
|
||||||
State::Pending(context) => context,
|
State::Pending(context) => context,
|
||||||
};
|
};
|
||||||
|
|
||||||
match exercise.mode {
|
match exercise.mode {
|
||||||
Mode::Compile => success!("Successfully ran {}!", exercise),
|
Mode::Compile => success!("Successfully ran {}!", exercise),
|
||||||
Mode::Test => success!("Successfully tested {}!", exercise),
|
Mode::Test => success!("Successfully tested {}!", exercise),
|
||||||
@ -167,7 +167,6 @@ fn prompt_for_completion(exercise: &Exercise, prompt_output: Option<String>) ->
|
|||||||
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!();
|
println!();
|
||||||
if no_emoji {
|
if no_emoji {
|
||||||
println!("~*~ {success_msg} ~*~")
|
println!("~*~ {success_msg} ~*~")
|
||||||
@ -183,6 +182,13 @@ fn prompt_for_completion(exercise: &Exercise, prompt_output: Option<String>) ->
|
|||||||
println!("{}", separator());
|
println!("{}", separator());
|
||||||
println!();
|
println!();
|
||||||
}
|
}
|
||||||
|
if success_hints {
|
||||||
|
println!("Hints:");
|
||||||
|
println!("{}", separator());
|
||||||
|
println!("{}", exercise.hint);
|
||||||
|
println!("{}", separator());
|
||||||
|
println!();
|
||||||
|
}
|
||||||
|
|
||||||
println!("You can keep working on this exercise,");
|
println!("You can keep working on this exercise,");
|
||||||
println!(
|
println!(
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user