[
  {
    "path": ".cargo/config.toml",
    "content": "[alias]\ndev = [\"run\", \"--\", \"dev\"]\n"
  },
  {
    "path": ".github/workflows/rust.yml",
    "content": "name: Check\n\non:\n  push:\n    branches: [main]\n    paths-ignore:\n      - website\n      - '*.md'\n  pull_request:\n    branches: [main]\n    paths-ignore:\n      - website\n      - '*.md'\n\nenv:\n  CARGO_TERM_COLOR: always\n\njobs:\n  clippy:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v6\n      - name: Clippy\n        run: cargo clippy -- --deny warnings\n  fmt:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v6\n      - name: rustfmt\n        run: cargo fmt --all --check\n  test:\n    runs-on: ${{ matrix.os }}\n    strategy:\n      matrix:\n        os: [ubuntu-latest, windows-latest, macos-latest]\n    steps:\n      - uses: actions/checkout@v6\n      - uses: swatinem/rust-cache@v2\n      - name: cargo test\n        env:\n          RUST_BACKTRACE: 1\n        run: cargo test --workspace\n  dev-check:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v6\n      - uses: swatinem/rust-cache@v2\n      - name: rustlings dev check\n        run: cargo dev check --require-solutions\n"
  },
  {
    "path": ".github/workflows/website.yml",
    "content": "name: Website\n\non:\n  workflow_dispatch:\n  push:\n    branches: [main]\n    paths: [website]\n\njobs:\n  build:\n    defaults:\n      run:\n        working-directory: website\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v6\n      - name: Install TailwindCSS\n        run: npm install\n      - name: Build CSS\n        run: npx @tailwindcss/cli -m -i input.css -o static/main.css\n      - name: Download Zola\n        run: curl -fsSL https://github.com/getzola/zola/releases/download/v0.22.1/zola-v0.22.1-x86_64-unknown-linux-gnu.tar.gz | tar xz\n      - name: Build site\n        run: ./zola build\n      - name: Upload static files as artifact\n        uses: actions/upload-pages-artifact@v4\n        with:\n          path: website/public/\n\n  deploy:\n    needs: build\n    # Grant GITHUB_TOKEN the permissions required to make a Pages deployment\n    permissions:\n      pages: write    # to deploy to Pages\n      id-token: write # to verify the deployment originates from an appropriate source\n    # Deploy to the github-pages environment\n    environment:\n      name: github-pages\n      url: ${{ steps.deployment.outputs.page_url }}\n    runs-on: ubuntu-latest\n    steps:\n      - name: Deploy to GitHub Pages\n        uses: actions/deploy-pages@v4\n"
  },
  {
    "path": ".gitignore",
    "content": "# Cargo\ntarget/\nCargo.lock\n!/Cargo.lock\n\n# State file\n.rustlings-state.txt\n\n# OS\n.DS_Store\n.direnv/\n\n# Editor\n*.swp\n.idea\n*.iml\n\n# Ignore file for editors like Helix\n.ignore\n"
  },
  {
    "path": ".typos.toml",
    "content": "[default.extend-words]\n\"earch\" = \"earch\" # Because of <s>earch in the list footer\n\n[files]\nextend-exclude = [\n  \"CHANGELOG.md\",\n]\n"
  },
  {
    "path": "CHANGELOG.md",
    "content": "## Unreleased\n\n### Changed\n\n- `vecs2`: Removed the use of `map` and `collect`, which are only taught later.\n\n## 6.5.0 (2025-08-21)\n\n### Added\n\n- Check that Clippy is installed before initialization\n\n### Changed\n\n- Upgrade to Rust edition 2024\n- Raise the minimum supported Rust version to `1.88`\n- Don't follow symlinks in the file watcher\n- `dev new`: Don't add `.rustlings-state.txt` to `.gitignore`\n\n### Fixed\n\n- Fix file links in VS Code\n- Fix error printing when the progress bar is shown\n- `dev check`: Don't check formatting if there are no solution files\n\n## 6.4.0 (2024-11-11)\n\n### Added\n\n- The list of exercises is now searchable by pressing `s` or `/` 🔍️ (thanks to [@frroossst](https://github.com/frroossst))\n- New option `c` in the prompt to manually check all exercises ✅ (thanks to [@Nahor](https://github.com/Nahor))\n- New command `check-all` to manually check all exercises ✅ (thanks to [@Nahor](https://github.com/Nahor))\n- Addictive animation for showing the progress of checking all exercises. A nice showcase of parallelism in Rust ✨\n- New option `x` in the prompt to reset the file of the current exercise 🔄\n- Allow `dead_code` for all exercises and solutions ⚰️ (thanks to [@huss4in](https://github.com/huss4in))\n- Pause input while running an exercise to avoid unexpected prompt interactions ⏸️\n- Limit the maximum number of exercises to 999. Any community exercises willing to reach that limit? 🔝\n\n### Changed\n\n- `enums3`: Remove redundant enum definition task (thanks to [@senekor](https://github.com/senekor))\n- `if2`: Make the exercise less confusing by avoiding \"fizz\", \"fuzz\", \"foo\", \"bar\" and \"baz\" (thanks to [@senekor](https://github.com/senekor))\n- `hashmap3`: Use the method `Entry::or_default`.\n- Update the state of all exercises when checking all of them (thanks to [@Nahor](https://github.com/Nahor))\n- The main prompt doesn't need a confirmation with ENTER on Unix-like systems anymore.\n- No more jumping back to a previous exercise when its file is changed. Use the list to jump between exercises.\n- Dump the solution file after an exercise is done even if the solution's directory doesn't exist.\n- Rework the footer in the list.\n- Optimize the file watcher.\n\n### Fixed\n\n- Fix bad contrast in the list on terminals with a light theme.\n\n## 6.3.0 (2024-08-29)\n\n### Added\n\n- Add the following exercise lints:\n  - `forbid(unsafe_code)`: You shouldn't write unsafe code in Rustlings.\n  - `forbid(unstable_features)`: You don't need unstable features in Rustlings and shouldn't rely on them while learning Rust.\n  - `forbid(todo)`: You forgot a `todo!()`.\n  - `forbid(empty_loop)`: This can only happen by mistake in Rustlings.\n  - `deny(infinite_loop)`: No infinite loops are needed in Rustlings.\n  - `deny(mem_forget)`: You shouldn't leak memory while still learning Rust.\n- Show a link to every exercise file in the list.\n- Add scroll padding in the list.\n- Break the help footer of the list into two lines when the terminal width isn't big enough.\n- Enable scrolling with the mouse in the list.\n- `dev check`: Show the progress of checks.\n- `dev check`: Check that the length of all exercise names is lower than 32.\n- `dev check`: Check if exercise contains no tests and isn't marked with `test = false`.\n\n### Changed\n\n- The compilation time when installing Rustlings is reduced.\n- Pressing `c` in the list for \"continue on\" now quits the list after setting the selected exercise as the current one.\n- Better highlighting of the solution file after an exercise is done.\n- Don't show the output of successful tests anymore. Instead, show the pretty output for tests.\n- Be explicit about `q` only quitting the list and not the whole program in the list.\n- Be explicit about `r` only resetting one exercise (the selected one) in the list.\n- Ignore the standard output of `git init`.\n- `threads3`: Remove the queue length and improve tests.\n- `errors4`: Use match instead of a comparison chain in the solution.\n- `functions3`: Only take `u8` to avoid using a too high number of iterations by mistake.\n- `dev check`: Always check with strict Clippy (warnings to errors) when checking the solutions.\n\n### Fixed\n\n- Fix the error on some systems about too many open files during the final check of all exercises.\n- Fix the list when the terminal height is too low.\n- Restore the terminal after an error in the list.\n\n## 6.2.0 (2024-08-09)\n\n### Added\n\n- Show a message before checking and running an exercise. This gives the user instant feedback and avoids confusion if the checks take too long.\n- Show a helpful error message when trying to install Rustlings with a Rust version lower than the minimum one that Rustlings supports.\n- Add a `README.md` file to the `solutions/` directory.\n- Allow initializing Rustlings in a Cargo workspace.\n- `dev check`: Check that all solutions are formatted with `rustfmt`.\n\n### Changed\n\n- Remove the state file and the solutions directory from the generated `.gitignore` file.\n- Run the final check of all exercises in parallel.\n- Small exercise improvements.\n\n## 6.1.0 (2024-07-10)\n\n#### Added\n\n- `dev check`: Check that all exercises (including community ones) include at least one `TODO` comment.\n- `dev check`: Check that all exercises actually fail to run (not already solved).\n\n#### Changed\n\n- Make enum variants more consistent between enum exercises.\n- `iterators3`: Teach about the possible case of integer overflow during division.\n\n#### Fixed\n\n- Exit with a helpful error message on missing/unsupported terminal/TTY.\n- Mark the last exercise as done.\n\n## 6.0.1 (2024-07-04)\n\nSmall exercise improvements and fixes.\nMost importantly, fixed that the exercise `clippy1` was already solved 😅\n\n## 6.0.0 (2024-07-03)\n\nThis release is the result of a complete rewrite to deliver a ton of new features and improvements ✨\nThe most important changes are highlighted below.\n\n### Installation\n\nThe installation has been simplified a lot!\nTo install Rustlings after installing Rust, all what you need to do now is running the following command:\n\n```bash\ncargo install rustlings\n```\n\nYes, this means that Rustlings is now on [crates.io](https://crates.io/crates/rustlings) 🎉\n\nYou can read about the motivations of this change in [this issue](https://github.com/rust-lang/rustlings/issues/1919).\n\n### UI/UX\n\n- The UI is now responsive when the terminal is resized.\n- The progress bar was moved to the bottom so that you can always see your progress and the current exercise to work on.\n- The current exercise path is now a terminal link. It will open the exercise file in your default editor when you click on it.\n- A small prompt is now always shown at the bottom. It allows you to choose an action by entering a character. For example, entering `h` will show you the hint of the current exercise.\n- The comment \"I AM NOT DONE!\" doesn't exist anymore. Instead of needing to remove it to go to the next exercise, you need to enter `n` in the terminal.\n\n### List mode\n\nA new list mode was added!\nYou can enter it by entering `l` in the watch mode.\nIt offers the following features:\n\n- Browse all exercises and see their state (pending/done).\n- Filter exercises based on their state (pending/done).\n- Continue at another exercise. This allows you to skip some exercises or go back to previous ones.\n- Reset an exercise so you can start over and revert your changes.\n\n### Solutions\n\nAfter finishing an exercise, a solution file will be available and Rustlings will show you its path in green.\nThis allows you to compare your solution with an idiomatic solution and maybe learn about other ways to solve a problem.\n\nWhile writing the solutions, all exercises have been polished 🌟\nFor example, every exercise now contains `TODO` comments to highlight what the user needs to change and where.\n\n### LSP support out of the box\n\nInstead of creating a `project.json` file using `rustlings lsp`, Rustlings now works with a `Cargo.toml` file out of the box.\nNo actions are needed to activate the language server `rust-analyzer`.\n\nThis should avoid issues related to the language server or to running exercises, especially the ones with Clippy.\n\n### Clippy\n\nClippy lints are now shown on all exercises, not only the Clippy exercises 📎\nMake Clippy your friend from early on 🥰\n\n### Community Exercises\n\nRustlings now supports community exercises!\n\nDo you want to create your own set of Rustlings exercises to focus on some specific topic?\nOr do you want to translate the original Rustlings exercises?\nThen follow the link to the guide about [community exercises](https://rustlings.rust-lang.org/community-exercises)!\n\n## 5.6.1 (2023-09-18)\n\n#### Changed\n\n- Converted all exercises with assertions to test mode.\n\n#### Fixed\n\n- `cow1`: Reverted regression introduced by calling `to_mut` where it\n  shouldn't have been called, and clarified comment.\n- `primitive_types3`: Require at least an array of 100 elements.\n- Removed hint comments when no hint exists for the exercise.\n- `as_ref_mut`: Fixed a typo in a test function name.\n- `enums3`: Fixed formatting with `rustfmt`.\n\n## 5.6.0 (2023-09-04)\n\n#### Added\n\n- New exercise: `if3`, teaching the user about `if let` statements.\n- `hashmaps2`: Added an extra test function to check if the amount of fruits is higher than zero.\n- `enums3`: Added a test for `Message`.\n- `if1`: Added a test case to check equal values.\n- `if3`: Added a note specifying that there are no test changes needed.\n\n#### Changed\n\n- Swapped the order of threads and smart pointer exercises.\n- Rewrote the CLI to use `clap` - it's matured much since we switched to `argh` :)\n- `structs3`: Switched from i32 to u32.\n- `move_semantics`: Switched 1-4 to tests, and rewrote them to be way simpler, while still teaching about the same\n  concepts.\n\n#### Fixed\n\n- `iterators5`:\n  - Removed an outdated part of the hint.\n  - Renamed variables to use snake_case.\n- `vecs2`: Updated the hint to reference the renamed loop variable.\n- `enums3`: Changed message string in test so that it gets properly tested.\n- `strings2`: Corrected line number in hint, then removed it (this both happened as part of this release cycle).\n- `primitive_types4`: Updated hint to the correct ending index.\n- `quiz1`: Removed duplicated sentence from exercise comments.\n- `errors4`: Improved comment.\n- `from_into`: Fixed test values.\n- `cow1`: Added `.to_mut()` to distinguish from the previous test case.\n- `threads2`: Updated hint text to reference the correct book heading.\n\n#### Housekeeping\n\n- Cleaned up the explanation paragraphs at the start of each exercise.\n- Lots of Nix housekeeping that I don't feel qualified to write about!\n- Improved CI workflows, we're now testing on multiple platforms at once.\n\n## 5.5.1 (2023-05-17)\n\n#### Fixed\n\n- Reverted `rust-project.json` path generation due to an upstream `rust-analyzer` fix.\n\n## 5.5.0 (2023-05-17)\n\n#### Added\n\n- `strings2`: Added a reference to the book chapter for reference conversion\n- `lifetimes`: Added a link to the lifetimekata project\n- Added a new `tests4` exercises, which teaches about testing for panics\n- Added a `!` prefix command to watch mode that runs an external command\n- Added a `--success-hints` option to watch mode that shows hints on exercise success\n\n#### Changed\n\n- `vecs2`: Renamed iterator variable bindings for clarify\n- `lifetimes`: Changed order of book references\n- `hashmaps2`: Clarified instructions in the todo block\n- Moved lifetime exercises before test exercises (via the recommended book ordering)\n- `options2`: Improved tests for layering options\n- `modules2`: Added more information to the hint\n\n#### Fixed\n\n- `errors2`: Corrected a comment wording\n- `iterators2`: Fixed a spelling mistake in the hint text\n- `variables`: Wrapped the mut keyword with backticks for readability\n- `move_semantics2`: Removed references to line numbers\n- `cow1`: Clarified the `owned_no_mutation` comments\n- `options3`: Changed exercise to panic when no match is found\n- `rustlings lsp` now generates absolute paths, which should fix VSCode `rust-analyzer` usage on Windows\n\n#### Housekeeping\n\n- Added a markdown linter to run on GitHub actions\n- Split quick installation section into two code blocks\n\n## 5.4.1 (2023-03-10)\n\n#### Changed\n\n- `vecs`: Added links to `iter_mut` and `map` to README.md\n- `cow1`: Changed main to tests\n- `iterators1`: Formatted according to rustfmt\n\n#### Fixed\n\n- `errors5`: Unified undisclosed type notation\n- `arc1`: Improved readability by avoiding implicit dereference\n- `macros4`: Prevented auto-fix by adding `#[rustfmt::skip]`\n- `cli`: Actually show correct progress percentages\n\n## 5.4.0 (2023-02-12)\n\n#### Changed\n\n- Reordered exercises\n  - Unwrapped `standard_library_types` into `iterators` and `smart_pointers`\n  - Moved smart pointer exercises behind threads\n  - Ordered `rc1` before `arc1`\n- **intro1**: Added a note on `rustlings lsp`\n- **threads1**: Panic if threads are not joined\n- **cli**:\n  - Made progress bar update proportional to amount of files verified\n  - Decreased `watch` delay from 2 to 1 second\n\n#### Fixed\n\n- Capitalized \"Rust\" in exercise hints\n- **enums3**: Removed superfluous tuple brackets\n- **quiz2, clippy1, iterators1**: Fixed a typo\n- **rc1**: Fixed a prompt error\n- **cli**:\n  - Fixed a typo in a method name\n  - Specified the edition in `rustc` commands\n\n#### Housekeeping\n\n- Bumped min Rust version to 1.58 in installation script\n\n## 5.3.0 (2022-12-23)\n\n#### Added\n\n- **cli**: Added a percentage display in watch mode\n- Added a `flake.nix` for Nix users\n\n#### Changed\n\n- **structs3**: Added an additional test\n- **macros**: Added a link to MacroKata in the README\n\n#### Fixed\n\n- **strings3**: Added a link to `std` in the hint\n- **threads1**: Corrected a hint link\n- **iterators1**: Clarified hint steps\n- **errors5**: Fix a typo in the hint\n- **options1**: Clarified on the usage of the 24-hour system\n- **threads2, threads3**: Explicitly use `Arc::clone`\n- **structs3**: Clarifed the hint\n- **quiz2, as_ref_mut, options1, traits1, traits2**: Clarified hints\n- **traits1, traits2, cli**: Tidied up unmatching backticks\n- **enums2**: Removed unnecessary indirection of self\n- **enums3**: Added an extra tuple comment\n\n#### Housekeeping\n\n- Added a VSCode extension recommendation\n- Applied some Clippy and rustfmt formatting\n- Added a note on Windows PowerShell and other shell compatibility\n\n## 5.2.1 (2022-09-06)\n\n#### Fixed\n\n- **quiz1**: Reworded the comment to actually reflect what's going on in the tests.\n  Also added another assert just to make sure.\n- **rc1**: Fixed a typo in the hint.\n- **lifetimes**: Add quotes to the `println!` output, for readability.\n\n#### Housekeeping\n\n- Fixed a typo in README.md\n\n## 5.2.0 (2022-08-27)\n\n#### Added\n\n- Added a `reset` command\n\n#### Changed\n\n- **options2**: Convert the exercise to use tests\n\n#### Fixed\n\n- **threads3**: Fixed a typo\n- **quiz1**: Adjusted the explanations to be consistent with\n  the tests\n\n## 5.1.1 (2022-08-17)\n\n#### Bug Fixes\n\n- Fixed an incorrect assertion in options1\n\n## 5.1.0 (2022-08-16)\n\n#### Features\n\n- Added a new `rc1` exercise.\n- Added a new `cow1` exercise.\n\n#### Bug Fixes\n\n- **variables5**: Corrected reference to previous exercise\n- **functions4**: Fixed line number reference\n- **strings3**: Clarified comment wording\n- **traits4, traits5**: Fixed line number reference\n- **traits5**:\n  - Fixed typo in \"parameter\"\n  - Made exercise prefer a traits-based solution\n- **lifetimes2**: Improved hint\n- **threads3**: Fixed typo in hint\n- **box1**: Replaced `unimplemented!` with `todo!`\n- **errors5**: Provided an explanation for usage of `Box<dyn Error>`\n- **quiz2**: Fixed a typo\n- **macros**: Updated the macros book link\n- **options1**:\n  - Removed unused code\n  - Added more granular tests\n- Fixed some comment syntax shenanigans in info.toml\n\n#### Housekeeping\n\n- Fixed a typo in .editorconfig\n- Fixed a typo in integration_tests.rs\n- Clarified manual installation instructions using `cargo install --path .`\n- Added a link to our Zulip in the readme file\n\n## 5.0.0 (2022-07-16)\n\n#### Features\n\n- Hint comments in exercises now also include a reference to the\n  `hint` watch mode subcommand.\n- **intro1**: Added more hints to point the user to the source file.\n- **variables**: Switched variables3 and variables4.\n- Moved `vec` and `primitive_types` exercises before `move_semantics`.\n- Renamed `vec` to `vecs` to be more in line with the naming in general.\n- Split up the `collections` exercises in their own folders.\n- **vec2**: Added a second part of the function that provides an alternative,\n  immutable way of modifying vec values.\n- **enums3**: Added a hint.\n- Moved `strings` before `modules`.\n- Added a `strings3` exercise to teach modifying strings.\n- Added a `hashmaps3` exercise for some advanced usage of hashmaps.\n- Moved the original `quiz2` to be `strings4`, since it only tested strings\n  anyways.\n- Reworked `quiz2` into a new exercise that tests more chapters.\n- Renamed `option` to `options`.\n- **options1**: Rewrote parts of the exercise to remove the weird array\n  iteration stuff.\n- Moved `generics3` to be `quiz3`.\n- Moved box/arc exercises behind `iterators`.\n- **iterators4**: Added a test for factorials of zero.\n- Split `threads1` between two exercises, the first one focusing more on\n  `JoinHandle`s.\n- Added a `threads3` exercises that uses `std::sync::mpsc`.\n- Added a `clippy3` exercises with some more interesting checks.\n- **as_ref_mut**: Added a section that actually tests `AsMut`.\n- Added 3 new lifetimes exercises.\n- Added 3 new traits exercises.\n\n#### Bug Fixes\n\n- **variables2**: Made output messages more verbose.\n- **variables5**: Added a nudging hint about shadowing.\n- **variables6**: Fixed link to book.\n- **functions**: Clarified the README wording. Generally cleaned up\n  some hints and added some extra comments.\n- **if2**: Renamed function name to `foo_if_fizz`.\n- **move_semantics**: Clarified some hints.\n- **quiz1**: Renamed the function name to be more verbose.\n- **structs1**: Use an integer type instead of strings. Renamed \"unit structs\"\n  to \"unit-like structs\", as is used in the book.\n- **structs3**: Added the `panic!` statement in from the beginning.\n- **errors1**: Use `is_empty()` instead of `len() > 0`\n- **errors3**: Improved the hint.\n- **errors5**: Improved exercise instructions and the hint.\n- **errors6**: Provided the skeleton of one of the functions that's supposed\n  to be implemented.\n- **iterators3**: Inserted `todo!` into `divide()` to keep a compiler error\n  from happening.\n- **from_str**: Added a hint comment about string error message conversion with\n  `Box<dyn Error>`.\n- **try_from_into**: Fixed the function name in comment.\n\n#### Removed\n\n- Removed the legacy LSP feature that was using `mod.rs` files.\n- Removed `quiz4`.\n- Removed `advanced_errs`. These were the last exercises in the recommended\n  order, and I've always felt like they didn't quite fit in with the mostly\n  simple, book-following style we've had in Rustlings.\n\n#### Housekeeping\n\n- Added missing exercises to the book index.\n- Updated spacing in Cargo.toml.\n- Added a GitHub actions config so that tests run on every PR/commit.\n\n## 4.8.0 (2022-07-01)\n\n#### Features\n\n- Added a progress indicator for `rustlings watch`.\n- The installation script now checks for Rustup being installed.\n- Added a `rustlings lsp` command to enable `rust-analyzer`.\n\n#### Bug Fixes\n\n- **move_semantics5**: Replaced \"in vogue\" with \"in scope\" in hint.\n- **if2**: Fixed a typo in the hint.\n- **variables1**: Fixed an incorrect line reference in the hint.\n- Fixed an out of bounds check in the installation Bash script.\n\n#### Housekeeping\n\n- Replaced the git.io URL with the fully qualified URL because of git.io's sunsetting.\n- Removed the deprecated Rust GitPod extension.\n\n## 4.7.1 (2022-04-20)\n\n#### Features\n\n- The amount of dependency crates that need to be compiled went down from ~65 to\n  ~45 by bumping dependency versions.\n- The minimum Rust version in the install scripts has been bumped to 1.56.0 (this isn't in\n  the release itself, since install scripts don't really get versioned)\n\n#### Bug Fixes\n\n- **arc1**: A small part has been rewritten using a more functional code style (#968).\n- **using_as**: A small part has been refactored to use `sum` instead of `fold`, resulting\n  in better readability.\n\n#### Housekeeping\n\n- The changelog will now be manually written instead of being automatically generated by the\n  Git log.\n\n## 4.7.0 (2022-04-14)\n\n#### Features\n\n- Add move_semantics6.rs exercise (#908) ([3f0e1303](https://github.com/rust-lang/rustlings/commit/3f0e1303e0b3bf3fecc0baced3c8b8a37f83c184))\n- **intro:** Add intro section. ([21c9f441](https://github.com/rust-lang/rustlings/commit/21c9f44168394e08338fd470b5f49b1fd235986f))\n- Include exercises folder in the project structure behind a feature, enabling rust-analyzer to work (#917) ([179a75a6](https://github.com/rust-lang/rustlings/commit/179a75a68d03ac9518dec2297fb17f91a4fc506b))\n\n#### Bug Fixes\n\n- Fix a few spelling mistakes ([1c0fe3cb](https://github.com/rust-lang/rustlings/commit/1c0fe3cbcca85f90b3985985b8e265ee872a2ab2))\n- **cli:**\n  - Move long text strings into constants. ([f78c4802](https://github.com/rust-lang/rustlings/commit/f78c48020830d7900dd8d81f355606581670446d))\n  - Replace `filter_map()` with `find_map()` ([9b27e8d](https://github.com/rust-lang/rustlings/commit/9b27e8d993ca20232fe38a412750c3f845a83b65))\n- **clippy1:**\n  - Set clippy::float_cmp lint to deny (#907) ([71a06044](https://github.com/rust-lang/rustlings/commit/71a06044e6a96ff756dc31d7b0ed665ae4badb57))\n  - Updated code to test correctness clippy lint with approx_constant lint rule ([f2650de3](https://github.com/rust-lang/rustlings/commit/f2650de369810867d2763e935ac0963c32ec420e))\n- **errors1:**\n  - Add a comment to make the purpose more clear (#486) ([cbcde345](https://github.com/rust-lang/rustlings/commit/cbcde345409c3e550112e449242848eaa3391bb6))\n  - Don't modify tests (#958) ([60bb7cc](https://github.com/rust-lang/rustlings/commit/60bb7cc3931d21d3986ad52b2b302e632a93831c))\n- **errors6:** Remove existing answer code ([43d0623](https://github.com/rust-lang/rustlings/commit/43d0623086edbc46fe896ba59c7afa22c3da9f7a))\n- **functions5:** Remove wrong new line and small English improvements (#885) ([8ef4869b](https://github.com/rust-lang/rustlings/commit/8ef4869b264094e5a9b50452b4534823a9df19c3))\n- **install:** protect path with whitespaces using quotes and stop at the first error ([d114847f](https://github.com/rust-lang/rustlings/commit/d114847f256c5f571c0b4c87e04b04bce3435509))\n- **intro1:** Add compiler error explanation. ([9b8de655](https://github.com/rust-lang/rustlings/commit/9b8de65525a5576b78cf0c8e4098cdd34296338f))\n- **iterators1:** reorder TODO steps ([0bd7a063](https://github.com/rust-lang/rustlings/commit/0bd7a0631a17a9d69af5746795a30efc9cf64e6e))\n- **move_semantics2:** Add comment ([89650f80](https://github.com/rust-lang/rustlings/commit/89650f808af23a32c9a2c6d46592b77547a6a464))\n- **move_semantics5:** correct typo (#857) ([46c28d5c](https://github.com/rust-lang/rustlings/commit/46c28d5cef3d8446b5a356b19d8dbc725f91a3a0))\n- **quiz1:** update to say quiz covers \"If\" ([1622e8c1](https://github.com/rust-lang/rustlings/commit/1622e8c198d89739765c915203efff0091bdeb78))\n- **structs3:**\n  - Add a hint for panic (#608) ([4f7ff5d9](https://github.com/rust-lang/rustlings/commit/4f7ff5d9c7b2d8b045194c1a9469d37e30257c4a))\n  - remove redundant 'return' (#852) ([bf33829d](https://github.com/rust-lang/rustlings/commit/bf33829da240375d086f96267fc2e02fa6b07001))\n  - Assigned value to `cents_per_gram` in test ([d1ee2da](https://github.com/rust-lang/rustlings/commit/d1ee2daf14f19105e6db3f9c610f44293d688532))\n- **structs3.rs:** assigned value to cents_per_gram in test ([d1ee2daf](https://github.com/rust-lang/rustlings/commit/d1ee2daf14f19105e6db3f9c610f44293d688532))\n- **traits1:** rename test functions to snake case (#854) ([1663a16e](https://github.com/rust-lang/rustlings/commit/1663a16eade6ca646b6ed061735f7982434d530d))\n\n#### Documentation improvements\n\n- Add hints on how to get GCC installed (#741) ([bc56861](https://github.com/rust-lang/rustlings/commit/bc5686174463ad6f4f6b824b0e9b97c3039d4886))\n- Fix some code blocks that were not highlighted ([17f9d74](https://github.com/rust-lang/rustlings/commit/17f9d7429ccd133a72e815fb5618e0ce79560929))\n\n## 4.6.0 (2021-09-25)\n\n#### Features\n\n- add advanced_errs2 ([abd6b70c](https://github.com/rust-lang/rustlings/commit/abd6b70c72dc6426752ff41f09160b839e5c449e))\n- add advanced_errs1 ([882d535b](https://github.com/rust-lang/rustlings/commit/882d535ba8628d5e0b37e8664b3e2f26260b2671))\n- Add a farewell message when quitting `watch` ([1caef0b4](https://github.com/rust-lang/rustlings/commit/1caef0b43494c8b8cdd6c9260147e70d510f1aca))\n- add more watch commands ([a7dc080b](https://github.com/rust-lang/rustlings/commit/a7dc080b95e49146fbaafe6922a6de2f8cb1582a), closes [#842](https://github.com/rust-lang/rustlings/issues/842))\n- **modules:** update exercises, add modules3 (#822) ([dfd2fab4](https://github.com/rust-lang/rustlings/commit/dfd2fab4f33d1bf59e2e5ee03123c0c9a67a9481))\n- **quiz1:** add default function name in comment (#838) ([0a11bad7](https://github.com/rust-lang/rustlings/commit/0a11bad71402b5403143d642f439f57931278c07))\n\n#### Bug Fixes\n\n- Correct small typo in exercises/conversions/from_str.rs ([86cc8529](https://github.com/rust-lang/rustlings/commit/86cc85295ae36948963ae52882e285d7e3e29323))\n- **cli:** typo in exercise.rs (#848) ([06d5c097](https://github.com/rust-lang/rustlings/commit/06d5c0973a3dffa3c6c6f70acb775d4c6630323c))\n- **from_str, try_from_into:** custom error types ([2dc93cad](https://github.com/rust-lang/rustlings/commit/2dc93caddad43821743e4903d89b355df58d7a49))\n- **modules2:** fix typo (#835) ([1c3beb0a](https://github.com/rust-lang/rustlings/commit/1c3beb0a59178c950dc05fe8ee2346b017429ae0))\n- **move_semantics5:**\n  - change &mut \\*y to &mut x (#814) ([d75759e8](https://github.com/rust-lang/rustlings/commit/d75759e829fdcd64ef071cf4b6eae2a011a7718b))\n  - Clarify instructions ([df25684c](https://github.com/rust-lang/rustlings/commit/df25684cb79f8413915e00b5efef29369849cef1))\n- **quiz1:** Fix inconsistent wording (#826) ([03131a3d](https://github.com/rust-lang/rustlings/commit/03131a3d35d9842598150f9da817f7cc26e2669a))\n\n## 4.5.0 (2021-07-07)\n\n#### Features\n\n- Add move_semantics5 exercise. (#746) ([399ab328](https://github.com/rust-lang/rustlings/commit/399ab328d8d407265c09563aa4ef4534b2503ff2))\n- **cli:** Add \"next\" to run the next unsolved exercise. (#785) ([d20e413a](https://github.com/rust-lang/rustlings/commit/d20e413a68772cd493561f2651cf244e822b7ca5))\n\n#### Bug Fixes\n\n- rename result1 to errors4 ([50ab289d](https://github.com/rust-lang/rustlings/commit/50ab289da6b9eb19a7486c341b00048c516b88c0))\n- move_semantics5 hints ([1b858285](https://github.com/rust-lang/rustlings/commit/1b85828548f46f58b622b5e0c00f8c989f928807))\n- remove trailing whitespaces from iterators1 ([4d4fa774](https://github.com/rust-lang/rustlings/commit/4d4fa77459392acd3581c6068aa8be9a02de12fc))\n- add hints to generics1 and generics2 exercises ([31457940](https://github.com/rust-lang/rustlings/commit/31457940846b3844d78d4a4d2b074bc8d6aaf1eb))\n- remove trailing whitespace ([d9b69bd1](https://github.com/rust-lang/rustlings/commit/d9b69bd1a0a7a99f2c0d80933ad2eea44c8c71b2))\n- **installation:** first PowerShell command ([aa9a943d](https://github.com/rust-lang/rustlings/commit/aa9a943ddf3ae260782e73c26bcc9db60e5894b6))\n- **iterators5:** derive Clone, Copy ([91fc9e31](https://github.com/rust-lang/rustlings/commit/91fc9e3118f4af603c9911698cc2a234725cb032))\n- **quiz1:** Updated question description (#794) ([d8766496](https://github.com/rust-lang/rustlings/commit/d876649616cc8a8dd5f539f8bc1a5434b960b1e9))\n- **try_from_into, from_str:** hints for dyn Error ([11d2cf0d](https://github.com/rust-lang/rustlings/commit/11d2cf0d604dee3f5023c17802d69438e69fa50e))\n- **variables5:** confine the answer further ([48ffcbd2](https://github.com/rust-lang/rustlings/commit/48ffcbd2c4cc4d936c2c7480019190f179813cc5))\n\n## 4.4.0 (2021-04-24)\n\n#### Bug Fixes\n\n- Fix spelling error in main.rs ([91ee27f2](https://github.com/rust-lang/rustlings/commit/91ee27f22bd3797a9db57e5fd430801c170c5db8))\n- typo in default out text ([644c49f1](https://github.com/rust-lang/rustlings/commit/644c49f1e04cbb24e95872b3a52b07d692ae3bc8))\n- **collections:** Naming exercises for vectors and hashmap ([bef39b12](https://github.com/rust-lang/rustlings/commit/bef39b125961310b34b34871e480a82e82af4678))\n- **from_str:**\n  - Correct typos ([5f7c89f8](https://github.com/rust-lang/rustlings/commit/5f7c89f85db1f33da01911eaa479c3a2d4721678))\n  - test for error instead of unwrap/should_panic ([15e71535](https://github.com/rust-lang/rustlings/commit/15e71535f37cfaed36e22eb778728d186e2104ab))\n  - use trait objects for from_str ([c3e7b831](https://github.com/rust-lang/rustlings/commit/c3e7b831786c9172ed8bd5d150f3c432f242fba9))\n- **functions3:** improve function argument type (#687) ([a6509cc4](https://github.com/rust-lang/rustlings/commit/a6509cc4d545d8825f01ddf7ee37823b372154dd))\n- **hashmap2:** Update incorrect assertion (#660) ([72aaa15e](https://github.com/rust-lang/rustlings/commit/72aaa15e6ab4b72b3422f1c6356396e20a2a2bb8))\n- **info:** Fix typo (#635) ([cddc1e86](https://github.com/rust-lang/rustlings/commit/cddc1e86e7ec744ee644cc774a4887b1a0ded3e8))\n- **iterators2:** Moved errors out of tests. ([baf4ba17](https://github.com/rust-lang/rustlings/commit/baf4ba175ba6eb92989e3dd54ecbec4bedc9a863), closes [#359](https://github.com/rust-lang/rustlings/issues/359))\n- **iterators3:** Enabled iterators3.rs to run without commented out tests. ([c6712dfc](https://github.com/rust-lang/rustlings/commit/c6712dfccd1a093e590ad22bbc4f49edc417dac0))\n- **main:** Let find_exercise work with borrows ([347f30bd](https://github.com/rust-lang/rustlings/commit/347f30bd867343c5ace1097e085a1f7e356553f7))\n- **move_semantics4:**\n  - Remove redundant \"instead\" (#640) ([cc266d7d](https://github.com/rust-lang/rustlings/commit/cc266d7d80b91e79df3f61984f231b7f1587218e))\n  - Small readbility improvement (#617) ([10965920](https://github.com/rust-lang/rustlings/commit/10965920fbdf8a1efc85bed869e55a1787006404))\n- **option2:** Rename uninformative variables (#675) ([b4de6594](https://github.com/rust-lang/rustlings/commit/b4de6594380636817d13c2677ec6f472a964cf43))\n- **quiz3:** Force an answer to Q2 (#672) ([0d894e6f](https://github.com/rust-lang/rustlings/commit/0d894e6ff739943901e1ae8c904582e5c2f843bd))\n- **structs:** Add 5.3 to structs/README (#652) ([6bd791f2](https://github.com/rust-lang/rustlings/commit/6bd791f2f44aa7f0ad926df767f6b1fa8f12a9a9))\n- **structs2:** correct grammar in hint (#663) ([ebdb66c7](https://github.com/rust-lang/rustlings/commit/ebdb66c7bfb6d687a14cc511a559a222e6fc5de4))\n- **structs3:**\n  - reword heading comment (#664) ([9f3e8c2d](https://github.com/rust-lang/rustlings/commit/9f3e8c2dde645e5264c2d2200e68842b5f47bfa3))\n  - add check to prevent naive implementation of is_international ([05a753fe](https://github.com/rust-lang/rustlings/commit/05a753fe6333d36dbee5f68c21dec04eacdc75df))\n- **threads1:** line number correction ([7857b0a6](https://github.com/rust-lang/rustlings/commit/7857b0a689b0847f48d8c14cbd1865e3b812d5ca))\n- **try_from_into:** use trait objects ([2e93a588](https://github.com/rust-lang/rustlings/commit/2e93a588e0abe8badb7eafafb9e7d073c2be5df8))\n\n#### Features\n\n- Replace clap with argh ([7928122f](https://github.com/rust-lang/rustlings/commit/7928122fcef9ca7834d988b1ec8ca0687478beeb))\n- Replace emojis when NO_EMOJI env variable present ([8d62a996](https://github.com/rust-lang/rustlings/commit/8d62a9963708dbecd9312e8bcc4b47049c72d155))\n- Added iterators5.rs exercise. ([b29ea17e](https://github.com/rust-lang/rustlings/commit/b29ea17ea94d1862114af2cf5ced0e09c197dc35))\n- **arc1:** Add more details to description and hint (#710) ([81be4044](https://github.com/rust-lang/rustlings/commit/81be40448777fa338ebced3b0bfc1b32d6370313))\n- **cli:** Improve the list command with options, and then some ([8bbe4ff1](https://github.com/rust-lang/rustlings/commit/8bbe4ff1385c5c169c90cd3ff9253f9a91daaf8e))\n- **list:**\n  - updated progress percentage ([1c6f7e4b](https://github.com/rust-lang/rustlings/commit/1c6f7e4b7b9b3bd36f4da2bb2b69c549cc8bd913))\n  - added progress info ([c0e3daac](https://github.com/rust-lang/rustlings/commit/c0e3daacaf6850811df5bc57fa43e0f249d5cfa4))\n\n## 4.3.0 (2020-12-29)\n\n#### Features\n\n- Rewrite default out text ([44d39112](https://github.com/rust-lang/rustlings/commit/44d39112ff122b29c9793fe52e605df1612c6490))\n- match exercise order to book chapters (#541) ([033bf119](https://github.com/rust-lang/rustlings/commit/033bf1198fc8bfce1b570e49da7cde010aa552e3))\n- Crab? (#586) ([fa9f522b](https://github.com/rust-lang/rustlings/commit/fa9f522b7f043d7ef73a39f003a9272dfe72c4f4))\n- add \"rustlings list\" command ([838f9f30](https://github.com/rust-lang/rustlings/commit/838f9f30083d0b23fd67503dcf0fbeca498e6647))\n- **try_from_into:** remove duplicate annotation ([04f1d079](https://github.com/rust-lang/rustlings/commit/04f1d079aa42a2f49af694bc92c67d731d31a53f))\n\n#### Bug Fixes\n\n- update structs README ([bcf14cf6](https://github.com/rust-lang/rustlings/commit/bcf14cf677adb3a38a3ac3ca53f3c69f61153025))\n- added missing exercises to info.toml ([90cfb6ff](https://github.com/rust-lang/rustlings/commit/90cfb6ff28377531bfc34acb70547bdb13374f6b))\n- gives a bit more context to magic number ([30644c9a](https://github.com/rust-lang/rustlings/commit/30644c9a062b825c0ea89435dc59f0cad86b110e))\n- **functions2:** Change signature to trigger precise error message: (#605) ([0ef95947](https://github.com/rust-lang/rustlings/commit/0ef95947cc30482e63a7045be6cc2fb6f6dcb4cc))\n- **structs1:** Adjust wording (#573) ([9334783d](https://github.com/rust-lang/rustlings/commit/9334783da31d821cc59174fbe8320df95828926c))\n- **try_from_into:**\n  - type error ([4f4cfcf3](https://github.com/rust-lang/rustlings/commit/4f4cfcf3c36c8718c7c170c9c3a6935e6ef0618c))\n  - Update description (#584) ([96347df9](https://github.com/rust-lang/rustlings/commit/96347df9df294f01153b29d9ad4ba361f665c755))\n- **vec1:** Have test compare every element in a and v ([9b6c6293](https://github.com/rust-lang/rustlings/commit/9b6c629397b24b944f484f5b2bbd8144266b5695))\n\n## 4.2.0 (2020-11-07)\n\n#### Features\n\n- Add HashMap exercises ([633c00cf](https://github.com/rust-lang/rustlings/commit/633c00cf8071e1e82959a3010452a32f34f29fc9))\n- Add Vec exercises ([0c12fa31](https://github.com/rust-lang/rustlings/commit/0c12fa31c57c03c6287458a0a8aca7afd057baf6))\n- **primitive_types6:** Add a test (#548) ([2b1fb2b7](https://github.com/rust-lang/rustlings/commit/2b1fb2b739bf9ad8d6b7b12af25fee173011bfc4))\n- **try_from_into:** Add tests (#571) ([95ccd926](https://github.com/rust-lang/rustlings/commit/95ccd92616ae79ba287cce221101e0bbe4f68cdc))\n\n#### Bug Fixes\n\n- log error output when inotify limit is exceeded ([d61b4e5a](https://github.com/rust-lang/rustlings/commit/d61b4e5a13b44d72d004082f523fa1b6b24c1aca))\n- more unique temp_file ([5643ef05](https://github.com/rust-lang/rustlings/commit/5643ef05bc81e4a840e9456f4406a769abbe1392))\n- **installation:** Update the MinRustVersion ([21bfb2d4](https://github.com/rust-lang/rustlings/commit/21bfb2d4777429c87d8d3b5fbf0ce66006dcd034))\n- **iterators2:** Update description (#578) ([197d3a3d](https://github.com/rust-lang/rustlings/commit/197d3a3d8961b2465579218a6749b2b2cefa8ddd))\n- **primitive_types6:**\n  - remove 'unused doc comment' warning ([472d8592](https://github.com/rust-lang/rustlings/commit/472d8592d65c8275332a20dfc269e7ac0d41bc88))\n  - missing comma in test ([4fb230da](https://github.com/rust-lang/rustlings/commit/4fb230daf1251444fcf29e085cee222a91f8a37e))\n- **quiz3:** Second test is for odd numbers, not even. (#553) ([18e0bfef](https://github.com/rust-lang/rustlings/commit/18e0bfef1de53071e353ba1ec5837002ff7290e6))\n\n## 4.1.0 (2020-10-05)\n\n#### Bug Fixes\n\n- Update rustlings version in Cargo.lock ([1cc40bc9](https://github.com/rust-lang/rustlings/commit/1cc40bc9ce95c23d56f6d91fa1c4deb646231fef))\n- **arc1:** index mod should equal thread count ([b4062ef6](https://github.com/rust-lang/rustlings/commit/b4062ef6993e80dac107c4093ea85166ad3ee0fa))\n- **enums3:** Update Message::ChangeColor to take a tuple. (#457) ([4b6540c7](https://github.com/rust-lang/rustlings/commit/4b6540c71adabad647de8a09e57295e7c7c7d794))\n- **exercises:** adding question mark to quiz2 ([101072ab](https://github.com/rust-lang/rustlings/commit/101072ab9f8c80b40b8b88cb06cbe38aca2481c5))\n- **generics3:** clarify grade change ([47f7672c](https://github.com/rust-lang/rustlings/commit/47f7672c0307732056e7426e81d351f0dd7e22e5))\n- **structs3:** Small adjustment of variable name ([114b54cb](https://github.com/rust-lang/rustlings/commit/114b54cbdb977234b39e5f180d937c14c78bb8b2))\n- **using_as:** Add test so that proper type is returned. (#512) ([3286c5ec](https://github.com/rust-lang/rustlings/commit/3286c5ec19ea5fb7ded81d047da5f8594108a490))\n\n#### Features\n\n- Added iterators1.rs exercise ([9642f5a3](https://github.com/rust-lang/rustlings/commit/9642f5a3f686270a4f8f6ba969919ddbbc4f7fdd))\n- Add ability to run rustlings on repl.it (#471) ([8f7b5bd0](https://github.com/rust-lang/rustlings/commit/8f7b5bd00eb83542b959830ef55192d2d76db90a))\n- Add gitpod support (#473) ([4821a8be](https://github.com/rust-lang/rustlings/commit/4821a8be94af4f669042a06ab917934cfacd032f))\n- Remind the user of the hint option (#425) ([816b1f5e](https://github.com/rust-lang/rustlings/commit/816b1f5e85d6cc6e72673813a85d0ada2a8f84af))\n- Remind the user of the hint option (#425) ([9f61db5d](https://github.com/rust-lang/rustlings/commit/9f61db5dbe38538cf06571fcdd5f806e7901e83a))\n- **cli:** Added 'cls' command to 'watch' mode (#474) ([4f2468e1](https://github.com/rust-lang/rustlings/commit/4f2468e14f574a93a2e9b688367b5752ed96ae7b))\n- **try_from_into:** Add insufficient length test (#469) ([523d18b8](https://github.com/rust-lang/rustlings/commit/523d18b873a319f7c09262f44bd40e2fab1830e5))\n\n## 4.0.0 (2020-07-08)\n\n#### Breaking Changes\n\n- Add a --nocapture option to display test harnesses' outputs ([8ad5f9bf](https://github.com/rust-lang/rustlings/commit/8ad5f9bf531a4848b1104b7b389a20171624c82f))\n- Rename test to quiz, fixes #244 ([010a0456](https://github.com/rust-lang/rustlings/commit/010a04569282149cea7f7a76fc4d7f4c9f0f08dd))\n\n#### Features\n\n- Add traits README ([173bb141](https://github.com/rust-lang/rustlings/commit/173bb14140c5530cbdb59e53ace3991a99d804af))\n- Add box1.rs exercise ([7479a473](https://github.com/rust-lang/rustlings/commit/7479a4737bdcac347322ad0883ca528c8675e720))\n- Rewrite try_from_into (#393) ([763aa6e3](https://github.com/rust-lang/rustlings/commit/763aa6e378a586caae2d8d63755a85eeba227933))\n- Add if2 exercise ([1da84b5f](https://github.com/rust-lang/rustlings/commit/1da84b5f7c489f65bd683c244f13c7d1ee812df0))\n- Added exercise structs3.rs ([b66e2e09](https://github.com/rust-lang/rustlings/commit/b66e2e09622243e086a0f1258dd27e1a2d61c891))\n- Add exercise variables6 covering const (#352) ([5999acd2](https://github.com/rust-lang/rustlings/commit/5999acd24a4f203292be36e0fd18d385887ec481))\n\n#### Bug Fixes\n\n- Change then to than ([ddd98ad7](https://github.com/rust-lang/rustlings/commit/ddd98ad75d3668fbb10eff74374148aa5ed2344d))\n- rename quiz1 to tests1 in info (#420) ([0dd1c6ca](https://github.com/rust-lang/rustlings/commit/0dd1c6ca6b389789e0972aa955fe17aa15c95f29))\n- fix quiz naming inconsistency (#421) ([5563adbb](https://github.com/rust-lang/rustlings/commit/5563adbb890587fc48fbbc9c4028642687f1e85b))\n- confine the user further in variable exercises ([06ef4cc6](https://github.com/rust-lang/rustlings/commit/06ef4cc654e75d22a526812919ee49b8956280bf))\n- update iterator and macro text for typos and clarity ([95900828](https://github.com/rust-lang/rustlings/commit/959008284834bece0196a01e17ac69a7e3590116))\n- update generics2 closes #362 ([964c974a](https://github.com/rust-lang/rustlings/commit/964c974a0274199d755073b917c2bc5da0c9b4f1))\n- confusing comment in conversions/try_from_into.rs ([c9e4f2cf](https://github.com/rust-lang/rustlings/commit/c9e4f2cfb4c48d0b7451263cfb43b9426438122d))\n- **arc1:** Passively introduce attributes (#429) ([113cdae2](https://github.com/rust-lang/rustlings/commit/113cdae2d4e4c55905e8056ad326ede7fd7de356))\n- **box1:** fix comment typo (#426) ([bb2ca251](https://github.com/rust-lang/rustlings/commit/bb2ca251106b27a7272d9a30872904dd1376654c))\n- **errorsn:** Try harder to confine the user. (#388) ([2b20c8a0](https://github.com/rust-lang/rustlings/commit/2b20c8a0f5774d07c58d110d75879f33fc6273b5))\n- **from_into.rs:** typo ([a901499e](https://github.com/rust-lang/rustlings/commit/a901499ededd3ce1995164700514fe4e9a0373ea))\n- **generics2:** Guide students to the answer (#430) ([e6bd8021](https://github.com/rust-lang/rustlings/commit/e6bd8021d9a7dd06feebc30c9d5f953901d7b419))\n- **installation:**\n  - Provide a backup git reference when tag can't be curl ([9e4fb100](https://github.com/rust-lang/rustlings/commit/9e4fb1009f1c9e3433915c03e22c2af422e5c5fe))\n  - Check if python is available while checking for git,rustc and cargo ([9cfb617d](https://github.com/rust-lang/rustlings/commit/9cfb617d5b0451b4b51644a1298965390cda9884))\n- **option1:**\n  - Don't add only zeros to the numbers array ([cce6a442](https://github.com/rust-lang/rustlings/commit/cce6a4427718724a9096800754cd3abeca6a1580))\n  - Add cast to usize, as it is confusing in the context of an exercise about Option ([f6cffc7e](https://github.com/rust-lang/rustlings/commit/f6cffc7e487b42f15a6f958e49704c93a8d4465b))\n- **option2:** Add TODO to comments (#400) ([10967bce](https://github.com/rust-lang/rustlings/commit/10967bce57682812dc0891a9f9757da1a9d87404))\n- **options1:** Add hint about Array Initialization (#389) ([9f75554f](https://github.com/rust-lang/rustlings/commit/9f75554f2a30295996f03f0160b98c0458305502))\n- **test2:** name of type String and &str (#394) ([d6c0a688](https://github.com/rust-lang/rustlings/commit/d6c0a688e6a96f93ad60d540d4b326f342fc0d45))\n- **variables6:** minor typo (#419) ([524e17df](https://github.com/rust-lang/rustlings/commit/524e17df10db95f7b90a0f75cc8997182a8a4094))\n\n## 3.0.0 (2020-04-11)\n\n#### Breaking Changes\n\n- make \"compile\" exercises print output (#278) ([3b6d5c](https://github.com/fmoko/rustlings/commit/3b6d5c3aaa27a242a832799eb66e96897d26fde3))\n\n#### Bug Fixes\n\n- **primitive_types:** revert primitive_types4 (#296) ([b3a3351e](https://github.com/rust-lang/rustlings/commit/b3a3351e8e6a0bdee07077d7b0382953821649ae))\n- **run:** compile clippy exercise files (#295) ([3ab084a4](https://github.com/rust-lang/rustlings/commit/3ab084a421c0f140ae83bf1fc3f47b39342e7373))\n- **conversions:**\n  - add additional test to meet exercise rules (#284) ([bc22ec3](https://github.com/fmoko/rustlings/commit/bc22ec382f843347333ef1301fc1bad773657f38))\n  - remove duplicate not done comment (#292) ([dab90f](https://github.com/fmoko/rustlings/commit/dab90f7b91a6000fe874e3d664f244048e5fa342))\n- don't hardcode documentation version for traits (#288) ([30e6af](https://github.com/fmoko/rustlings/commit/30e6af60690c326fb5d3a9b7335f35c69c09137d))\n\n#### Features\n\n- add Option2 exercise (#290) ([86b5c08b](https://github.com/rust-lang/rustlings/commit/86b5c08b9bea1576127a7c5f599f5752072c087d))\n- add exercise for option (#282) ([135e5d47](https://github.com/rust-lang/rustlings/commit/135e5d47a7c395aece6f6022117fb20c82f2d3d4))\n- add new exercises for generics (#280) ([76be5e4e](https://github.com/rust-lang/rustlings/commit/76be5e4e991160f5fd9093f03ee2ba260e8f7229))\n- **ci:** add buildkite config ([b049fa2c](https://github.com/rust-lang/rustlings/commit/b049fa2c84dba0f0c8906ac44e28fd45fba51a71))\n\n### 2.2.1 (2020-02-27)\n\n#### Bug Fixes\n\n- Re-add cloning the repo to install scripts ([3d9b03c5](https://github.com/rust-lang/rustlings/commit/3d9b03c52b8dc51b140757f6fd25ad87b5782ef5))\n\n#### Features\n\n- Add clippy lints (#269) ([1e2fd9c9](https://github.com/rust-lang/rustlings/commit/1e2fd9c92f8cd6e389525ca1a999fca4c90b5921))\n\n## 2.2.0 (2020-02-25)\n\n#### Bug Fixes\n\n- Update deps to version compatible with aarch64-pc-windows (#263) ([19a93428](https://github.com/rust-lang/rustlings/commit/19a93428b3c73d994292671f829bdc8e5b7b3401))\n- **docs:**\n  - Added a necessary step to Windows installation process (#242) ([3906efcd](https://github.com/rust-lang/rustlings/commit/3906efcd52a004047b460ed548037093de3f523f))\n  - Fixed mangled sentence from book; edited for clarity (#266) ([ade52ff](https://github.com/rust-lang/rustlings/commit/ade52ffb739987287ddd5705944c8777705faed9))\n  - Updated iterators readme to account for iterators4 exercise (#273) ([bec8e3a](https://github.com/rust-lang/rustlings/commit/bec8e3a644cbd88db1c73ea5f1d8a364f4a34016))\n- **installation:** make fatal errors more obvious (#272) ([17d0951e](https://github.com/rust-lang/rustlings/commit/17d0951e66fda8e11b204d5c4c41a0d5e22e78f7))\n- **iterators2:**\n  - Remove reference to missing iterators2.rs (#245) ([419f7797](https://github.com/rust-lang/rustlings/commit/419f7797f294e4ce6a2b883199731b5bde77d262))\n- **as_ref_mut:** Enable a test and improve per clippy's suggestion (#256) ([dfdf809](https://github.com/rust-lang/rustlings/commit/dfdf8093ebbd4145864995627b812780de52f902))\n- **tests1:**\n  - Change test command ([fe10e06c](https://github.com/rust-lang/rustlings/commit/fe10e06c3733ddb4a21e90d09bf79bfe618e97ce)\n  - Correct test command in tests1.rs comment (#263) ([39fa7ae](https://github.com/rust-lang/rustlings/commit/39fa7ae8b70ad468da49b06f11b2383135a63bcf))\n\n#### Features\n\n- Add variables5.rs exercise (#264) ([0c73609e](https://github.com/rust-lang/rustlings/commit/0c73609e6f2311295e95d6f96f8c747cfc4cba03))\n- Show a completion message when watching (#253) ([d25ee55a](https://github.com/rust-lang/rustlings/commit/d25ee55a3205882d35782e370af855051b39c58c))\n- Add type conversion and parsing exercises (#249) ([0c85dc11](https://github.com/rust-lang/rustlings/commit/0c85dc1193978b5165491b99cc4922caf8d14a65))\n- Created consistent money unit (#258) ([fd57f8f](https://github.com/rust-lang/rustlings/commit/fd57f8f2c1da2af8ddbebbccec214e6f40f4dbab))\n- Enable test for exercise test4 (#276) ([8b971ff](https://github.com/rust-lang/rustlings/commit/8b971ffab6079a706ac925f5917f987932b55c07))\n- Added traits exercises (#274 but specifically #216, which originally added\n  this :heart:) ([b559cdd](https://github.com/rust-lang/rustlings/commit/b559cdd73f32c0d0cfc1feda39f82b3e3583df17))\n\n## 2.1.0 (2019-11-27)\n\n#### Bug Fixes\n\n- add line numbers in several exercises and hints ([b565c4d3](https://github.com/rust-lang/rustlings/commit/b565c4d3e74e8e110bef201a082fa1302722a7c3))\n- **arc1:** Fix some words in the comment ([c42c3b21](https://github.com/rust-lang/rustlings/commit/c42c3b2101df9164c8cd7bb344def921e5ba3e61))\n- **enums:** Add link to chapter on pattern syntax (#242) ([615ce327](https://github.com/rust-lang/rustlings/commit/615ce3279800c56d89f19d218ccb7ef576624feb))\n- **primitive_types4:**\n  - update outdated hint ([4c5189df](https://github.com/rust-lang/rustlings/commit/4c5189df2bdd9a231f6b2611919ba5aa14da0d3f))\n  - update outdated comment ([ded2c034](https://github.com/rust-lang/rustlings/commit/ded2c034ba93fa1e3c2c2ea16b83abc1a57265e8))\n- **strings2:** update line number in hint ([a09f684f](https://github.com/rust-lang/rustlings/commit/a09f684f05c58d239a6fc59ec5f81c2533e8b820))\n- **variables1:** Correct wrong word in comment ([fda5a470](https://github.com/rust-lang/rustlings/commit/fda5a47069e0954f16a04e8e50945e03becb71a5))\n\n#### Features\n\n- **watch:** show hint while watching ([8143d57b](https://github.com/rust-lang/rustlings/commit/8143d57b4e88c51341dd4a18a14c536042cc009c))\n\n## 2.0.0 (2019-11-12)\n\n#### Bug Fixes\n\n- **default:** Clarify the installation procedure ([c371b853](https://github.com/rust-lang/rustlings/commit/c371b853afa08947ddeebec0edd074b171eeaae0))\n- **info:** Fix trailing newlines for hints ([795b6e34](https://github.com/rust-lang/rustlings/commit/795b6e348094a898e9227a14f6232f7bb94c8d31))\n- **run:** make `run` never prompt ([4b265465](https://github.com/rust-lang/rustlings/commit/4b26546589f7d2b50455429482cf1f386ceae8b3))\n\n#### Breaking Changes\n\n- Refactor hint system ([9bdb0a12](https://github.com/rust-lang/rustlings/commit/9bdb0a12e45a8e9f9f6a4bd4a9c172c5376c7f60))\n- improve `watch` execution mode ([2cdd6129](https://github.com/rust-lang/rustlings/commit/2cdd61294f0d9a53775ee24ad76435bec8a21e60))\n- Index exercises by name ([627cdc07](https://github.com/rust-lang/rustlings/commit/627cdc07d07dfe6a740e885e0ddf6900e7ec336b))\n- **run:** makes `run` never prompt ([4b265465](https://github.com/rust-lang/rustlings/commit/4b26546589f7d2b50455429482cf1f386ceae8b3))\n\n#### Features\n\n- **cli:** check for rustc before doing anything ([36a033b8](https://github.com/rust-lang/rustlings/commit/36a033b87a6549c1e5639c908bf7381c84f4f425))\n- **hint:** Add test for hint ([ce9fa6eb](https://github.com/rust-lang/rustlings/commit/ce9fa6ebbfdc3e7585d488d9409797285708316f))\n\n### 1.5.1 (2019-11-11)\n\n#### Bug Fixes\n\n- **errors3:** Update hint ([dcfb427b](https://github.com/rust-lang/rustlings/commit/dcfb427b09585f0193f0a294443fdf99f11c64cb), closes [#185](https://github.com/rust-lang/rustlings/issues/185))\n- **if1:** Remove `return` reference ([ad03d180](https://github.com/rust-lang/rustlings/commit/ad03d180c9311c0093e56a3531eec1a9a70cdb45))\n- **strings:** Move Strings before Structs ([6dcecb38](https://github.com/rust-lang/rustlings/commit/6dcecb38a4435593beb87c8e12d6314143631482), closes [#204](https://github.com/rust-lang/rustlings/issues/204))\n- **structs1:** Remove misleading comment ([f72e5a8f](https://github.com/rust-lang/rustlings/commit/f72e5a8f05568dde04eaeac10b9a69872f21cb37))\n- **threads:** Move Threads behind SLT ([fbe91a67](https://github.com/rust-lang/rustlings/commit/fbe91a67a482bfe64cbcdd58d06ba830a0f39da3), closes [#205](https://github.com/rust-lang/rustlings/issues/205))\n- **watch:** clear screen before each `verify()` ([3aff590](https://github.com/rust-lang/rustlings/commit/3aff59085586c24196a547c2693adbdcf4432648))\n\n## 1.5.0 (2019-11-09)\n\n#### Bug Fixes\n\n- **test1:** Rewrite logic ([79a56942](https://github.com/rust-lang/rustlings/commit/79a569422c8309cfc9e4aed25bf4ab3b3859996b))\n- **installation:** Fix rustlings installation check ([7a252c47](https://github.com/rust-lang/rustlings/commit/7a252c475551486efb52f949b8af55803b700bc6))\n- **iterators:** Rename iterator3.rs ([433d2115](https://github.com/rust-lang/rustlings/commit/433d2115bc1c04b6d34a335a18c9a8f3e2672bc6))\n- **iterators2:** Remove syntax resulting in misleading error message ([4cde8664](https://github.com/rust-lang/rustlings/commit/4cde86643e12db162a66e62f23b78962986046ac))\n- **option1:**\n  - Fix arguments passed to assert! macro (#222) ([4c2cf6da](https://github.com/rust-lang/rustlings/commit/4c2cf6da755efe02725e05ecc3a303304c10a6da))\n  - Fix arguments passed to assert! macro ([ead4f7af](https://github.com/rust-lang/rustlings/commit/ead4f7af9e10e53418efdde5c359159347282afd))\n  - Add test for prematurely passing exercise ([a750e4a1](https://github.com/rust-lang/rustlings/commit/a750e4a1a3006227292bb17d57d78ce84da6bfc6))\n- **primitive_types4:** Fail on a slice covering the wrong area ([5b1e673c](https://github.com/rust-lang/rustlings/commit/5b1e673cec1658afc4ebbbc800213847804facf5))\n- **readme:** http to https ([70946b85](https://github.com/rust-lang/rustlings/commit/70946b85e536e80e70ed9505cb650ca0a3a1fbb5))\n- **test1:**\n  - Swap assertion parameter order ([4086d463](https://github.com/rust-lang/rustlings/commit/4086d463a981e81d97781851d17db2ced290f446))\n  - renamed function name to snake case closes #180 ([89d5186c](https://github.com/rust-lang/rustlings/commit/89d5186c0dae8135ecabf90ee8bb35949bc2d29b))\n\n#### Features\n\n- Add enums exercises ([dc150321](https://github.com/rust-lang/rustlings/commit/dc15032112fc485226a573a18139e5ce928b1755))\n- Added exercise for struct update syntax ([1c4c8764](https://github.com/rust-lang/rustlings/commit/1c4c8764ed118740cd4cee73272ddc6cceb9d959))\n- **iterators2:** adds iterators2 exercise including config ([9288fccf](https://github.com/rust-lang/rustlings/commit/9288fccf07a2c5043b76d0fd6491e4cf72d76031))\n\n### 1.4.1 (2019-08-13)\n\n#### Bug Fixes\n\n- **iterators2:** Remove syntax resulting in misleading error message ([4cde8664](https://github.com/rust-lang/rustlings/commit/4cde86643e12db162a66e62f23b78962986046ac))\n- **option1:** Add test for prematurely passing exercise ([a750e4a1](https://github.com/rust-lang/rustlings/commit/a750e4a1a3006227292bb17d57d78ce84da6bfc6))\n- **test1:** Swap assertion parameter order ([4086d463](https://github.com/rust-lang/rustlings/commit/4086d463a981e81d97781851d17db2ced290f446))\n\n## 1.4.0 (2019-07-13)\n\n#### Bug Fixes\n\n- **installation:** Fix rustlings installation check ([7a252c47](https://github.com/rust-lang/rustlings/commit/7a252c475551486efb52f949b8af55803b700bc6))\n- **iterators:** Rename iterator3.rs ([433d2115](https://github.com/rust-lang/rustlings/commit/433d2115bc1c04b6d34a335a18c9a8f3e2672bc6))\n- **readme:** http to https ([70946b85](https://github.com/rust-lang/rustlings/commit/70946b85e536e80e70ed9505cb650ca0a3a1fbb5))\n- **test1:** renamed function name to snake case ([89d5186c](https://github.com/rust-lang/rustlings/commit/89d5186c0dae8135ecabf90ee8bb35949bc2d29b))\n- **cli:** Check if changed exercise file exists before calling verify ([ba85ca3](https://github.com/rust-lang/rustlings/commit/ba85ca32c4cfc61de46851ab89f9c58a28f33c88))\n- **structs1:** Fix the irrefutable let pattern warning ([cc6a141](https://github.com/rust-lang/rustlings/commit/cc6a14104d7c034eadc98297eaaa972d09c50b1f))\n\n#### Features\n\n- **changelog:** Use clog for changelogs ([34e31232](https://github.com/rust-lang/rustlings/commit/34e31232dfddde284a341c9609b33cd27d9d5724))\n- **iterators2:** adds iterators2 exercise including config ([9288fccf](https://github.com/rust-lang/rustlings/commit/9288fccf07a2c5043b76d0fd6491e4cf72d76031))\n\n### 1.3.0 (2019-06-05)\n\n#### Features\n\n- Adds a simple exercise for structures (#163, @briankung)\n\n#### Bug Fixes\n\n- Add Result type signature as it is difficult for new comers to understand Generics and Error all at once. (#157, @veggiemonk)\n- Rustfmt and whitespace fixes (#161, @eddyp)\n- errorsn.rs: Separate also the hints from each other to avoid accidental viewing (#162, @eddyp)\n- fixed outdated links (#165, @gushroom)\n- Fix broken link (#164, @HanKruiger)\n- Remove highlighting and syntect (#167, @komaeda)\n\n### 1.2.2 (2019-05-07)\n\n#### Bug Fixes\n\n- Reverted `--nocapture` flag since it was causing tests to pass unconditionally\n\n### 1.2.1 (2019-04-22)\n\n#### Bug Fixes\n\n- Fix the `--nocapture` feature (@komaeda)\n- Provide a nicer error message for when you're in the wrong directory\n\n### 1.2.0 (2019-04-22)\n\n#### Features\n\n- Add errors to exercises that compile without user changes (@yvan-sraka)\n- Use --nocapture when testing, enabling `println!` when running (@komaeda)\n\n### 1.1.1 (2019-04-14)\n\n#### Bug fixes\n\n- Fix permissions on exercise files (@zacanger, #133)\n- Make installation checks more thorough (@komaeda, 1b3469f236bc6979c27f6e1a04e4138a88e55de3)\n- Fix order of true/false in tests for executables (@mgeier, #137)\n- Stop run from panicking when compile fails (@cjpearce, #141)\n- Fix intermittent test failure caused by race condition (@cjpearce, #140)\n- Fix links by deleting book version (@diodfr, #142)\n- Canonicalize paths to fix path matching (@cjpearce, #143)\n\n### 1.1.0 (2019-03-20)\n\n- errors2.rs: update link to Rust book (#124)\n- Start verification at most recently modified file (#120)\n- Watch for file creation events in watch mode (#117)\n- Add standard library types to exercises suite (#119)\n- Give a warning when Rustlings isn't run from the right directory (#123)\n- Verify that rust version is recent enough to install Rustlings (#131)\n\n### 1.0.1 (2019-03-06)\n\n- Adds a way to install Rustlings in one command (`curl -L https://git.io/rustlings | bash`)\n- Makes `rustlings watch` react to create file events (@shaunbennett, #117)\n- Reworks the exercise management to use an external TOML file instead of just listing them in the code\n\n### 1.0.0 (2019-03-06)\n\nInitial release.\n"
  },
  {
    "path": "CONTRIBUTING.md",
    "content": "# Contributing to Rustlings\n\nFirst off, thanks for taking the time to contribute! ❤️\n\n## Quick Reference\n\nI want to …\n\n- _report a bug!_ ➡️ [open an issue](#issues)\n- _fix a bug!_ ➡️ [open a pull request](#pull-requests)\n- _implement a new feature!_ ➡️ [open an issue to discuss it first, then a pull request](#issues)\n- _add an exercise!_ ➡️ [read this](#adding-an-exercise)\n- _update an outdated exercise!_ ➡️ [open a pull request](#pull-requests)\n\n## Issues\n\nYou can open an issue [here](https://github.com/rust-lang/rustlings/issues/new).\nIf you're reporting a bug, please include the output of the following commands:\n\n- `cargo --version`\n- `rustlings --version`\n- `ls -la`\n- Your OS name and version\n\n## Pull Requests\n\nYou are welcome to open a pull request, but unless it is small and trivial, **please open an issue to discuss your idea first** 🙏🏼\n\nOpening a pull request is as easy as forking the repository and committing your changes.\nIf you need any help with it or face any Git related problems, don't hesitate to ask for help 🤗\n\nIt may take time to review your pull request.\nPlease be patient 😇\n\nWhen updating an exercise, check if its solution needs to be updated.\n\n## Adding An Exercise\n\n- Name the file `exercises/yourTopic/yourTopicN.rs`.\n- Make sure to put in some helpful links, and link to sections of The Book in `exercises/yourTopic/README.md`.\n- In the exercise, add a `// TODO: …` comment where user changes are required.\n- Add a solution at `solutions/yourTopic/yourTopicN.rs` with comments explaining it.\n- Add the [metadata for your exercise](#exercise-metadata) in the `rustlings-macros/info.toml` file.\n- Make sure your exercise runs with `rustlings run yourTopicN`.\n- [Open a pull request](#pull-requests).\n\n### Exercise Metadata\n\nThe exercise metadata should contain the following:\n\n```toml\n[[exercises]]\nname = \"yourTopicN\"\ndir = \"yourTopic\"\nhint = \"\"\"\nA useful (multi-line) hint for your exercise.\nInclude links to a section in The Book or a documentation page.\"\"\"\n```\n\nIf your exercise doesn't contain any test, add `test = false` to the exercise metadata.\nBut adding tests is recommended.\n"
  },
  {
    "path": "Cargo.lock",
    "content": "# This file is automatically @generated by Cargo.\n# It is not intended for manual editing.\nversion = 4\n\n[[package]]\nname = \"anstream\"\nversion = \"1.0.0\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"824a212faf96e9acacdbd09febd34438f8f711fb84e09a8916013cd7815ca28d\"\ndependencies = [\n \"anstyle\",\n \"anstyle-parse\",\n \"anstyle-query\",\n \"anstyle-wincon\",\n \"colorchoice\",\n \"is_terminal_polyfill\",\n \"utf8parse\",\n]\n\n[[package]]\nname = \"anstyle\"\nversion = \"1.0.14\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"940b3a0ca603d1eade50a4846a2afffd5ef57a9feac2c0e2ec2e14f9ead76000\"\n\n[[package]]\nname = \"anstyle-parse\"\nversion = \"1.0.0\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"52ce7f38b242319f7cabaa6813055467063ecdc9d355bbb4ce0c68908cd8130e\"\ndependencies = [\n \"utf8parse\",\n]\n\n[[package]]\nname = \"anstyle-query\"\nversion = \"1.1.5\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc\"\ndependencies = [\n \"windows-sys 0.61.2\",\n]\n\n[[package]]\nname = \"anstyle-wincon\"\nversion = \"3.0.11\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d\"\ndependencies = [\n \"anstyle\",\n \"once_cell_polyfill\",\n \"windows-sys 0.61.2\",\n]\n\n[[package]]\nname = \"anyhow\"\nversion = \"1.0.102\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c\"\n\n[[package]]\nname = \"bitflags\"\nversion = \"1.3.2\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a\"\n\n[[package]]\nname = \"bitflags\"\nversion = \"2.11.0\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"843867be96c8daad0d758b57df9392b6d8d271134fce549de6ce169ff98a92af\"\n\n[[package]]\nname = \"cfg-if\"\nversion = \"1.0.4\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801\"\n\n[[package]]\nname = \"clap\"\nversion = \"4.6.0\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"b193af5b67834b676abd72466a96c1024e6a6ad978a1f484bd90b85c94041351\"\ndependencies = [\n \"clap_builder\",\n \"clap_derive\",\n]\n\n[[package]]\nname = \"clap_builder\"\nversion = \"4.6.0\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"714a53001bf66416adb0e2ef5ac857140e7dc3a0c48fb28b2f10762fc4b5069f\"\ndependencies = [\n \"anstream\",\n \"anstyle\",\n \"clap_lex\",\n \"strsim\",\n]\n\n[[package]]\nname = \"clap_derive\"\nversion = \"4.6.0\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"1110bd8a634a1ab8cb04345d8d878267d57c3cf1b38d91b71af6686408bbca6a\"\ndependencies = [\n \"heck\",\n \"proc-macro2\",\n \"quote\",\n \"syn\",\n]\n\n[[package]]\nname = \"clap_lex\"\nversion = \"1.1.0\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"c8d4a3bb8b1e0c1050499d1815f5ab16d04f0959b233085fb31653fbfc9d98f9\"\n\n[[package]]\nname = \"colorchoice\"\nversion = \"1.0.5\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"1d07550c9036bf2ae0c684c4297d503f838287c83c53686d05370d0e139ae570\"\n\n[[package]]\nname = \"crossterm\"\nversion = \"0.29.0\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"d8b9f2e4c67f833b660cdb0a3523065869fb35570177239812ed4c905aeff87b\"\ndependencies = [\n \"bitflags 2.11.0\",\n \"crossterm_winapi\",\n \"document-features\",\n \"mio\",\n \"parking_lot\",\n \"rustix\",\n \"signal-hook\",\n \"signal-hook-mio\",\n \"winapi\",\n]\n\n[[package]]\nname = \"crossterm_winapi\"\nversion = \"0.9.1\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"acdd7c62a3665c7f6830a51635d9ac9b23ed385797f70a83bb8bafe9c572ab2b\"\ndependencies = [\n \"winapi\",\n]\n\n[[package]]\nname = \"document-features\"\nversion = \"0.2.12\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"d4b8a88685455ed29a21542a33abd9cb6510b6b129abadabdcef0f4c55bc8f61\"\ndependencies = [\n \"litrs\",\n]\n\n[[package]]\nname = \"equivalent\"\nversion = \"1.0.2\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f\"\n\n[[package]]\nname = \"errno\"\nversion = \"0.3.14\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb\"\ndependencies = [\n \"libc\",\n \"windows-sys 0.61.2\",\n]\n\n[[package]]\nname = \"fastrand\"\nversion = \"2.3.0\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be\"\n\n[[package]]\nname = \"foldhash\"\nversion = \"0.1.5\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2\"\n\n[[package]]\nname = \"fsevent-sys\"\nversion = \"4.1.0\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"76ee7a02da4d231650c7cea31349b889be2f45ddb3ef3032d2ec8185f6313fd2\"\ndependencies = [\n \"libc\",\n]\n\n[[package]]\nname = \"getrandom\"\nversion = \"0.4.2\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"0de51e6874e94e7bf76d726fc5d13ba782deca734ff60d5bb2fb2607c7406555\"\ndependencies = [\n \"cfg-if\",\n \"libc\",\n \"r-efi\",\n \"wasip2\",\n \"wasip3\",\n]\n\n[[package]]\nname = \"hashbrown\"\nversion = \"0.15.5\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1\"\ndependencies = [\n \"foldhash\",\n]\n\n[[package]]\nname = \"hashbrown\"\nversion = \"0.16.1\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100\"\n\n[[package]]\nname = \"heck\"\nversion = \"0.5.0\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea\"\n\n[[package]]\nname = \"id-arena\"\nversion = \"2.3.0\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954\"\n\n[[package]]\nname = \"indexmap\"\nversion = \"2.13.0\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"7714e70437a7dc3ac8eb7e6f8df75fd8eb422675fc7678aff7364301092b1017\"\ndependencies = [\n \"equivalent\",\n \"hashbrown 0.16.1\",\n \"serde\",\n \"serde_core\",\n]\n\n[[package]]\nname = \"inotify\"\nversion = \"0.11.1\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"bd5b3eaf1a28b758ac0faa5a4254e8ab2705605496f1b1f3fbbc3988ad73d199\"\ndependencies = [\n \"bitflags 2.11.0\",\n \"inotify-sys\",\n \"libc\",\n]\n\n[[package]]\nname = \"inotify-sys\"\nversion = \"0.1.5\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"e05c02b5e89bff3b946cedeca278abc628fe811e604f027c45a8aa3cf793d0eb\"\ndependencies = [\n \"libc\",\n]\n\n[[package]]\nname = \"is_terminal_polyfill\"\nversion = \"1.70.2\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695\"\n\n[[package]]\nname = \"itoa\"\nversion = \"1.0.17\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"92ecc6618181def0457392ccd0ee51198e065e016d1d527a7ac1b6dc7c1f09d2\"\n\n[[package]]\nname = \"kqueue\"\nversion = \"1.1.1\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"eac30106d7dce88daf4a3fcb4879ea939476d5074a9b7ddd0fb97fa4bed5596a\"\ndependencies = [\n \"kqueue-sys\",\n \"libc\",\n]\n\n[[package]]\nname = \"kqueue-sys\"\nversion = \"1.0.4\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"ed9625ffda8729b85e45cf04090035ac368927b8cebc34898e7c120f52e4838b\"\ndependencies = [\n \"bitflags 1.3.2\",\n \"libc\",\n]\n\n[[package]]\nname = \"leb128fmt\"\nversion = \"0.1.0\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2\"\n\n[[package]]\nname = \"libc\"\nversion = \"0.2.183\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"b5b646652bf6661599e1da8901b3b9522896f01e736bad5f723fe7a3a27f899d\"\n\n[[package]]\nname = \"linux-raw-sys\"\nversion = \"0.12.1\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53\"\n\n[[package]]\nname = \"litrs\"\nversion = \"1.0.0\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"11d3d7f243d5c5a8b9bb5d6dd2b1602c0cb0b9db1621bafc7ed66e35ff9fe092\"\n\n[[package]]\nname = \"lock_api\"\nversion = \"0.4.14\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965\"\ndependencies = [\n \"scopeguard\",\n]\n\n[[package]]\nname = \"log\"\nversion = \"0.4.29\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897\"\n\n[[package]]\nname = \"memchr\"\nversion = \"2.8.0\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79\"\n\n[[package]]\nname = \"mio\"\nversion = \"1.1.1\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"a69bcab0ad47271a0234d9422b131806bf3968021e5dc9328caf2d4cd58557fc\"\ndependencies = [\n \"libc\",\n \"log\",\n \"wasi\",\n \"windows-sys 0.61.2\",\n]\n\n[[package]]\nname = \"notify\"\nversion = \"8.2.0\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"4d3d07927151ff8575b7087f245456e549fea62edf0ec4e565a5ee50c8402bc3\"\ndependencies = [\n \"bitflags 2.11.0\",\n \"fsevent-sys\",\n \"inotify\",\n \"kqueue\",\n \"libc\",\n \"log\",\n \"mio\",\n \"notify-types\",\n \"walkdir\",\n \"windows-sys 0.60.2\",\n]\n\n[[package]]\nname = \"notify-types\"\nversion = \"2.1.0\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"42b8cfee0e339a0337359f3c88165702ac6e600dc01c0cc9579a92d62b08477a\"\ndependencies = [\n \"bitflags 2.11.0\",\n]\n\n[[package]]\nname = \"once_cell\"\nversion = \"1.21.4\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50\"\n\n[[package]]\nname = \"once_cell_polyfill\"\nversion = \"1.70.2\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe\"\n\n[[package]]\nname = \"parking_lot\"\nversion = \"0.12.5\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a\"\ndependencies = [\n \"lock_api\",\n \"parking_lot_core\",\n]\n\n[[package]]\nname = \"parking_lot_core\"\nversion = \"0.9.12\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1\"\ndependencies = [\n \"cfg-if\",\n \"libc\",\n \"redox_syscall\",\n \"smallvec\",\n \"windows-link\",\n]\n\n[[package]]\nname = \"prettyplease\"\nversion = \"0.2.37\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b\"\ndependencies = [\n \"proc-macro2\",\n \"syn\",\n]\n\n[[package]]\nname = \"proc-macro2\"\nversion = \"1.0.106\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934\"\ndependencies = [\n \"unicode-ident\",\n]\n\n[[package]]\nname = \"quote\"\nversion = \"1.0.45\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924\"\ndependencies = [\n \"proc-macro2\",\n]\n\n[[package]]\nname = \"r-efi\"\nversion = \"6.0.0\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf\"\n\n[[package]]\nname = \"redox_syscall\"\nversion = \"0.5.18\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d\"\ndependencies = [\n \"bitflags 2.11.0\",\n]\n\n[[package]]\nname = \"rustix\"\nversion = \"1.1.4\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190\"\ndependencies = [\n \"bitflags 2.11.0\",\n \"errno\",\n \"libc\",\n \"linux-raw-sys\",\n \"windows-sys 0.61.2\",\n]\n\n[[package]]\nname = \"rustlings\"\nversion = \"6.5.0\"\ndependencies = [\n \"anyhow\",\n \"clap\",\n \"crossterm\",\n \"notify\",\n \"rustix\",\n \"rustlings-macros\",\n \"serde\",\n \"serde_json\",\n \"tempfile\",\n \"toml\",\n]\n\n[[package]]\nname = \"rustlings-macros\"\nversion = \"6.5.0\"\ndependencies = [\n \"quote\",\n \"serde\",\n \"toml\",\n]\n\n[[package]]\nname = \"same-file\"\nversion = \"1.0.6\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502\"\ndependencies = [\n \"winapi-util\",\n]\n\n[[package]]\nname = \"scopeguard\"\nversion = \"1.2.0\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49\"\n\n[[package]]\nname = \"semver\"\nversion = \"1.0.27\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2\"\n\n[[package]]\nname = \"serde\"\nversion = \"1.0.228\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e\"\ndependencies = [\n \"serde_core\",\n \"serde_derive\",\n]\n\n[[package]]\nname = \"serde_core\"\nversion = \"1.0.228\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad\"\ndependencies = [\n \"serde_derive\",\n]\n\n[[package]]\nname = \"serde_derive\"\nversion = \"1.0.228\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79\"\ndependencies = [\n \"proc-macro2\",\n \"quote\",\n \"syn\",\n]\n\n[[package]]\nname = \"serde_json\"\nversion = \"1.0.149\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86\"\ndependencies = [\n \"itoa\",\n \"memchr\",\n \"serde\",\n \"serde_core\",\n \"zmij\",\n]\n\n[[package]]\nname = \"serde_spanned\"\nversion = \"1.0.4\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"f8bbf91e5a4d6315eee45e704372590b30e260ee83af6639d64557f51b067776\"\ndependencies = [\n \"serde_core\",\n]\n\n[[package]]\nname = \"signal-hook\"\nversion = \"0.3.18\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"d881a16cf4426aa584979d30bd82cb33429027e42122b169753d6ef1085ed6e2\"\ndependencies = [\n \"libc\",\n \"signal-hook-registry\",\n]\n\n[[package]]\nname = \"signal-hook-mio\"\nversion = \"0.2.5\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"b75a19a7a740b25bc7944bdee6172368f988763b744e3d4dfe753f6b4ece40cc\"\ndependencies = [\n \"libc\",\n \"mio\",\n \"signal-hook\",\n]\n\n[[package]]\nname = \"signal-hook-registry\"\nversion = \"1.4.8\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"c4db69cba1110affc0e9f7bcd48bbf87b3f4fc7c61fc9155afd4c469eb3d6c1b\"\ndependencies = [\n \"errno\",\n \"libc\",\n]\n\n[[package]]\nname = \"smallvec\"\nversion = \"1.15.1\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03\"\n\n[[package]]\nname = \"strsim\"\nversion = \"0.11.1\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f\"\n\n[[package]]\nname = \"syn\"\nversion = \"2.0.117\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99\"\ndependencies = [\n \"proc-macro2\",\n \"quote\",\n \"unicode-ident\",\n]\n\n[[package]]\nname = \"tempfile\"\nversion = \"3.27.0\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd\"\ndependencies = [\n \"fastrand\",\n \"getrandom\",\n \"once_cell\",\n \"rustix\",\n \"windows-sys 0.61.2\",\n]\n\n[[package]]\nname = \"toml\"\nversion = \"1.0.6+spec-1.1.0\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"399b1124a3c9e16766831c6bba21e50192572cdd98706ea114f9502509686ffc\"\ndependencies = [\n \"indexmap\",\n \"serde_core\",\n \"serde_spanned\",\n \"toml_datetime\",\n \"toml_parser\",\n \"toml_writer\",\n \"winnow\",\n]\n\n[[package]]\nname = \"toml_datetime\"\nversion = \"1.0.0+spec-1.1.0\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"32c2555c699578a4f59f0cc68e5116c8d7cabbd45e1409b989d4be085b53f13e\"\ndependencies = [\n \"serde_core\",\n]\n\n[[package]]\nname = \"toml_parser\"\nversion = \"1.0.9+spec-1.1.0\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"702d4415e08923e7e1ef96cd5727c0dfed80b4d2fa25db9647fe5eb6f7c5a4c4\"\ndependencies = [\n \"winnow\",\n]\n\n[[package]]\nname = \"toml_writer\"\nversion = \"1.0.6+spec-1.1.0\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"ab16f14aed21ee8bfd8ec22513f7287cd4a91aa92e44edfe2c17ddd004e92607\"\n\n[[package]]\nname = \"unicode-ident\"\nversion = \"1.0.24\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75\"\n\n[[package]]\nname = \"unicode-xid\"\nversion = \"0.2.6\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853\"\n\n[[package]]\nname = \"utf8parse\"\nversion = \"0.2.2\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821\"\n\n[[package]]\nname = \"walkdir\"\nversion = \"2.5.0\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b\"\ndependencies = [\n \"same-file\",\n \"winapi-util\",\n]\n\n[[package]]\nname = \"wasi\"\nversion = \"0.11.1+wasi-snapshot-preview1\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b\"\n\n[[package]]\nname = \"wasip2\"\nversion = \"1.0.2+wasi-0.2.9\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"9517f9239f02c069db75e65f174b3da828fe5f5b945c4dd26bd25d89c03ebcf5\"\ndependencies = [\n \"wit-bindgen\",\n]\n\n[[package]]\nname = \"wasip3\"\nversion = \"0.4.0+wasi-0.3.0-rc-2026-01-06\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"5428f8bf88ea5ddc08faddef2ac4a67e390b88186c703ce6dbd955e1c145aca5\"\ndependencies = [\n \"wit-bindgen\",\n]\n\n[[package]]\nname = \"wasm-encoder\"\nversion = \"0.244.0\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"990065f2fe63003fe337b932cfb5e3b80e0b4d0f5ff650e6985b1048f62c8319\"\ndependencies = [\n \"leb128fmt\",\n \"wasmparser\",\n]\n\n[[package]]\nname = \"wasm-metadata\"\nversion = \"0.244.0\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"bb0e353e6a2fbdc176932bbaab493762eb1255a7900fe0fea1a2f96c296cc909\"\ndependencies = [\n \"anyhow\",\n \"indexmap\",\n \"wasm-encoder\",\n \"wasmparser\",\n]\n\n[[package]]\nname = \"wasmparser\"\nversion = \"0.244.0\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe\"\ndependencies = [\n \"bitflags 2.11.0\",\n \"hashbrown 0.15.5\",\n \"indexmap\",\n \"semver\",\n]\n\n[[package]]\nname = \"winapi\"\nversion = \"0.3.9\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419\"\ndependencies = [\n \"winapi-i686-pc-windows-gnu\",\n \"winapi-x86_64-pc-windows-gnu\",\n]\n\n[[package]]\nname = \"winapi-i686-pc-windows-gnu\"\nversion = \"0.4.0\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6\"\n\n[[package]]\nname = \"winapi-util\"\nversion = \"0.1.11\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22\"\ndependencies = [\n \"windows-sys 0.61.2\",\n]\n\n[[package]]\nname = \"winapi-x86_64-pc-windows-gnu\"\nversion = \"0.4.0\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f\"\n\n[[package]]\nname = \"windows-link\"\nversion = \"0.2.1\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5\"\n\n[[package]]\nname = \"windows-sys\"\nversion = \"0.60.2\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb\"\ndependencies = [\n \"windows-targets\",\n]\n\n[[package]]\nname = \"windows-sys\"\nversion = \"0.61.2\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc\"\ndependencies = [\n \"windows-link\",\n]\n\n[[package]]\nname = \"windows-targets\"\nversion = \"0.53.5\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3\"\ndependencies = [\n \"windows-link\",\n \"windows_aarch64_gnullvm\",\n \"windows_aarch64_msvc\",\n \"windows_i686_gnu\",\n \"windows_i686_gnullvm\",\n \"windows_i686_msvc\",\n \"windows_x86_64_gnu\",\n \"windows_x86_64_gnullvm\",\n \"windows_x86_64_msvc\",\n]\n\n[[package]]\nname = \"windows_aarch64_gnullvm\"\nversion = \"0.53.1\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53\"\n\n[[package]]\nname = \"windows_aarch64_msvc\"\nversion = \"0.53.1\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006\"\n\n[[package]]\nname = \"windows_i686_gnu\"\nversion = \"0.53.1\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3\"\n\n[[package]]\nname = \"windows_i686_gnullvm\"\nversion = \"0.53.1\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c\"\n\n[[package]]\nname = \"windows_i686_msvc\"\nversion = \"0.53.1\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2\"\n\n[[package]]\nname = \"windows_x86_64_gnu\"\nversion = \"0.53.1\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499\"\n\n[[package]]\nname = \"windows_x86_64_gnullvm\"\nversion = \"0.53.1\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1\"\n\n[[package]]\nname = \"windows_x86_64_msvc\"\nversion = \"0.53.1\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650\"\n\n[[package]]\nname = \"winnow\"\nversion = \"0.7.15\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"df79d97927682d2fd8adb29682d1140b343be4ac0f08fd68b7765d9c059d3945\"\n\n[[package]]\nname = \"wit-bindgen\"\nversion = \"0.51.0\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5\"\ndependencies = [\n \"wit-bindgen-rust-macro\",\n]\n\n[[package]]\nname = \"wit-bindgen-core\"\nversion = \"0.51.0\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"ea61de684c3ea68cb082b7a88508a8b27fcc8b797d738bfc99a82facf1d752dc\"\ndependencies = [\n \"anyhow\",\n \"heck\",\n \"wit-parser\",\n]\n\n[[package]]\nname = \"wit-bindgen-rust\"\nversion = \"0.51.0\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"b7c566e0f4b284dd6561c786d9cb0142da491f46a9fbed79ea69cdad5db17f21\"\ndependencies = [\n \"anyhow\",\n \"heck\",\n \"indexmap\",\n \"prettyplease\",\n \"syn\",\n \"wasm-metadata\",\n \"wit-bindgen-core\",\n \"wit-component\",\n]\n\n[[package]]\nname = \"wit-bindgen-rust-macro\"\nversion = \"0.51.0\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"0c0f9bfd77e6a48eccf51359e3ae77140a7f50b1e2ebfe62422d8afdaffab17a\"\ndependencies = [\n \"anyhow\",\n \"prettyplease\",\n \"proc-macro2\",\n \"quote\",\n \"syn\",\n \"wit-bindgen-core\",\n \"wit-bindgen-rust\",\n]\n\n[[package]]\nname = \"wit-component\"\nversion = \"0.244.0\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2\"\ndependencies = [\n \"anyhow\",\n \"bitflags 2.11.0\",\n \"indexmap\",\n \"log\",\n \"serde\",\n \"serde_derive\",\n \"serde_json\",\n \"wasm-encoder\",\n \"wasm-metadata\",\n \"wasmparser\",\n \"wit-parser\",\n]\n\n[[package]]\nname = \"wit-parser\"\nversion = \"0.244.0\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"ecc8ac4bc1dc3381b7f59c34f00b67e18f910c2c0f50015669dde7def656a736\"\ndependencies = [\n \"anyhow\",\n \"id-arena\",\n \"indexmap\",\n \"log\",\n \"semver\",\n \"serde\",\n \"serde_derive\",\n \"serde_json\",\n \"unicode-xid\",\n \"wasmparser\",\n]\n\n[[package]]\nname = \"zmij\"\nversion = \"1.0.21\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\nchecksum = \"b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa\"\n"
  },
  {
    "path": "Cargo.toml",
    "content": "[workspace]\nexclude = [\n  \"tests/test_exercises\",\n  \"dev\",\n]\n\n[workspace.package]\nversion = \"6.5.0\"\nauthors = [\n  \"Mo Bitar <mo8it@proton.me>\", # https://github.com/mo8it\n  \"Liv <mokou@fastmail.com>\", # https://github.com/shadows-withal\n  # Alumni\n  \"Carol (Nichols || Goulding) <carol.nichols@gmail.com>\", # https://github.com/carols10cents\n]\nrepository = \"https://github.com/rust-lang/rustlings\"\nlicense = \"MIT\"\nedition = \"2024\" # On Update: Update the edition of `rustfmt` in `dev check` and `CARGO_TOML` in `dev new`.\nrust-version = \"1.88\"\n\n[workspace.dependencies]\nserde = { version = \"1\", features = [\"derive\"] }\ntoml = { version = \"1\", default-features = false, features = [\"std\", \"parse\", \"serde\"] }\n\n[package]\nname = \"rustlings\"\ndescription = \"Small exercises to get you used to reading and writing Rust code!\"\nversion.workspace = true\nauthors.workspace = true\nrepository.workspace = true\nlicense.workspace = true\nedition.workspace = true\nrust-version.workspace = true\nkeywords = [\n  \"exercise\",\n  \"learning\",\n]\ninclude = [\n  \"/src/\",\n  \"/exercises/\",\n  \"/solutions/\",\n  # A symlink to be able to include `dev/Cargo.toml` although `dev` is excluded.\n  \"/dev-Cargo.toml\",\n  \"/README.md\",\n  \"/LICENSE\",\n]\n\n[dependencies]\nanyhow = \"1\"\nclap = { version = \"4\", features = [\"derive\"] }\ncrossterm = { version = \"0.29\", default-features = false, features = [\"windows\", \"events\"] }\nnotify = \"8\"\nrustlings-macros = { path = \"rustlings-macros\", version = \"=6.5.0\" }\nserde_json = \"1\"\nserde.workspace = true\ntoml.workspace = true\n\n[target.'cfg(not(windows))'.dependencies]\nrustix = { version = \"1.0\", default-features = false, features = [\"std\", \"stdio\", \"termios\"] }\n\n[dev-dependencies]\ntempfile = \"3\"\n\n[profile.release]\npanic = \"abort\"\n\n[profile.dev]\npanic = \"abort\"\n\n[package.metadata.release]\npre-release-hook = [\"./release-hook.sh\"]\npre-release-commit-message = \"Release 🎉\"\n\n[workspace.lints.rust]\nunsafe_code = \"forbid\"\nunstable_features = \"forbid\"\n\n[workspace.lints.clippy]\nempty_loop = \"forbid\"\ndisallowed-types = \"deny\"\ndisallowed-methods = \"deny\"\ninfinite_loop = \"deny\"\nmem_forget = \"deny\"\ndbg_macro = \"warn\"\ntodo = \"warn\"\n\n[lints]\nworkspace = true\n"
  },
  {
    "path": "LICENSE",
    "content": "The MIT License (MIT)\n\nCopyright (c) 2016 Carol (Nichols || Goulding)\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n\n"
  },
  {
    "path": "README.md",
    "content": "# [Rustlings](https://rustlings.rust-lang.org) 🦀\n\nSmall exercises to get you used to reading and writing [Rust](https://www.rust-lang.org) code - _Recommended in parallel to reading [the official Rust book](https://doc.rust-lang.org/book) 📚️_\n\nVisit the **website** for a demo, info about setup and more:\n\n## ➡️ [rustlings.rust-lang.org](https://rustlings.rust-lang.org) ⬅️\n"
  },
  {
    "path": "build.rs",
    "content": "fn main() {\n    // Fix building from source on Windows because it can't handle file links.\n    #[cfg(windows)]\n    let _ = std::fs::copy(\"dev/Cargo.toml\", \"dev-Cargo.toml\");\n}\n"
  },
  {
    "path": "clippy.toml",
    "content": "disallowed-types = [\n  { path = \"crossterm::style::Stylize\", reason = \"inefficient, use `.queue(…)` instead\" },\n  { path = \"crossterm::style::styled_content::StyledContent\", reason = \"inefficient, use `.queue(…)` instead\" },\n]\n\ndisallowed-methods = [\n  { path = \"crossterm::style::style\", reason = \"inefficient, use `.queue(…)` instead\" },\n  { path = \"std::thread::spawn\", replacement = \"std::thread::Builder::spawn\", reason = \"handle the error\" },\n  { path = \"std::thread::Scope::spawn\", replacement = \"std::thread::Builder::spawn\", reason = \"handle the error\" },\n  { path = \"std::process::exit\", replacement = \"std::process::ExitCode\" },\n]\n"
  },
  {
    "path": "dev/Cargo.toml",
    "content": "# Don't edit the `bin` list manually! It is updated by `cargo dev update`. This comment line will be stripped in `rustlings init`.\nbin = [\n  { name = \"intro1\", path = \"../exercises/00_intro/intro1.rs\" },\n  { name = \"intro1_sol\", path = \"../solutions/00_intro/intro1.rs\" },\n  { name = \"intro2\", path = \"../exercises/00_intro/intro2.rs\" },\n  { name = \"intro2_sol\", path = \"../solutions/00_intro/intro2.rs\" },\n  { name = \"variables1\", path = \"../exercises/01_variables/variables1.rs\" },\n  { name = \"variables1_sol\", path = \"../solutions/01_variables/variables1.rs\" },\n  { name = \"variables2\", path = \"../exercises/01_variables/variables2.rs\" },\n  { name = \"variables2_sol\", path = \"../solutions/01_variables/variables2.rs\" },\n  { name = \"variables3\", path = \"../exercises/01_variables/variables3.rs\" },\n  { name = \"variables3_sol\", path = \"../solutions/01_variables/variables3.rs\" },\n  { name = \"variables4\", path = \"../exercises/01_variables/variables4.rs\" },\n  { name = \"variables4_sol\", path = \"../solutions/01_variables/variables4.rs\" },\n  { name = \"variables5\", path = \"../exercises/01_variables/variables5.rs\" },\n  { name = \"variables5_sol\", path = \"../solutions/01_variables/variables5.rs\" },\n  { name = \"variables6\", path = \"../exercises/01_variables/variables6.rs\" },\n  { name = \"variables6_sol\", path = \"../solutions/01_variables/variables6.rs\" },\n  { name = \"functions1\", path = \"../exercises/02_functions/functions1.rs\" },\n  { name = \"functions1_sol\", path = \"../solutions/02_functions/functions1.rs\" },\n  { name = \"functions2\", path = \"../exercises/02_functions/functions2.rs\" },\n  { name = \"functions2_sol\", path = \"../solutions/02_functions/functions2.rs\" },\n  { name = \"functions3\", path = \"../exercises/02_functions/functions3.rs\" },\n  { name = \"functions3_sol\", path = \"../solutions/02_functions/functions3.rs\" },\n  { name = \"functions4\", path = \"../exercises/02_functions/functions4.rs\" },\n  { name = \"functions4_sol\", path = \"../solutions/02_functions/functions4.rs\" },\n  { name = \"functions5\", path = \"../exercises/02_functions/functions5.rs\" },\n  { name = \"functions5_sol\", path = \"../solutions/02_functions/functions5.rs\" },\n  { name = \"if1\", path = \"../exercises/03_if/if1.rs\" },\n  { name = \"if1_sol\", path = \"../solutions/03_if/if1.rs\" },\n  { name = \"if2\", path = \"../exercises/03_if/if2.rs\" },\n  { name = \"if2_sol\", path = \"../solutions/03_if/if2.rs\" },\n  { name = \"if3\", path = \"../exercises/03_if/if3.rs\" },\n  { name = \"if3_sol\", path = \"../solutions/03_if/if3.rs\" },\n  { name = \"quiz1\", path = \"../exercises/quizzes/quiz1.rs\" },\n  { name = \"quiz1_sol\", path = \"../solutions/quizzes/quiz1.rs\" },\n  { name = \"primitive_types1\", path = \"../exercises/04_primitive_types/primitive_types1.rs\" },\n  { name = \"primitive_types1_sol\", path = \"../solutions/04_primitive_types/primitive_types1.rs\" },\n  { name = \"primitive_types2\", path = \"../exercises/04_primitive_types/primitive_types2.rs\" },\n  { name = \"primitive_types2_sol\", path = \"../solutions/04_primitive_types/primitive_types2.rs\" },\n  { name = \"primitive_types3\", path = \"../exercises/04_primitive_types/primitive_types3.rs\" },\n  { name = \"primitive_types3_sol\", path = \"../solutions/04_primitive_types/primitive_types3.rs\" },\n  { name = \"primitive_types4\", path = \"../exercises/04_primitive_types/primitive_types4.rs\" },\n  { name = \"primitive_types4_sol\", path = \"../solutions/04_primitive_types/primitive_types4.rs\" },\n  { name = \"primitive_types5\", path = \"../exercises/04_primitive_types/primitive_types5.rs\" },\n  { name = \"primitive_types5_sol\", path = \"../solutions/04_primitive_types/primitive_types5.rs\" },\n  { name = \"primitive_types6\", path = \"../exercises/04_primitive_types/primitive_types6.rs\" },\n  { name = \"primitive_types6_sol\", path = \"../solutions/04_primitive_types/primitive_types6.rs\" },\n  { name = \"vecs1\", path = \"../exercises/05_vecs/vecs1.rs\" },\n  { name = \"vecs1_sol\", path = \"../solutions/05_vecs/vecs1.rs\" },\n  { name = \"vecs2\", path = \"../exercises/05_vecs/vecs2.rs\" },\n  { name = \"vecs2_sol\", path = \"../solutions/05_vecs/vecs2.rs\" },\n  { name = \"move_semantics1\", path = \"../exercises/06_move_semantics/move_semantics1.rs\" },\n  { name = \"move_semantics1_sol\", path = \"../solutions/06_move_semantics/move_semantics1.rs\" },\n  { name = \"move_semantics2\", path = \"../exercises/06_move_semantics/move_semantics2.rs\" },\n  { name = \"move_semantics2_sol\", path = \"../solutions/06_move_semantics/move_semantics2.rs\" },\n  { name = \"move_semantics3\", path = \"../exercises/06_move_semantics/move_semantics3.rs\" },\n  { name = \"move_semantics3_sol\", path = \"../solutions/06_move_semantics/move_semantics3.rs\" },\n  { name = \"move_semantics4\", path = \"../exercises/06_move_semantics/move_semantics4.rs\" },\n  { name = \"move_semantics4_sol\", path = \"../solutions/06_move_semantics/move_semantics4.rs\" },\n  { name = \"move_semantics5\", path = \"../exercises/06_move_semantics/move_semantics5.rs\" },\n  { name = \"move_semantics5_sol\", path = \"../solutions/06_move_semantics/move_semantics5.rs\" },\n  { name = \"structs1\", path = \"../exercises/07_structs/structs1.rs\" },\n  { name = \"structs1_sol\", path = \"../solutions/07_structs/structs1.rs\" },\n  { name = \"structs2\", path = \"../exercises/07_structs/structs2.rs\" },\n  { name = \"structs2_sol\", path = \"../solutions/07_structs/structs2.rs\" },\n  { name = \"structs3\", path = \"../exercises/07_structs/structs3.rs\" },\n  { name = \"structs3_sol\", path = \"../solutions/07_structs/structs3.rs\" },\n  { name = \"enums1\", path = \"../exercises/08_enums/enums1.rs\" },\n  { name = \"enums1_sol\", path = \"../solutions/08_enums/enums1.rs\" },\n  { name = \"enums2\", path = \"../exercises/08_enums/enums2.rs\" },\n  { name = \"enums2_sol\", path = \"../solutions/08_enums/enums2.rs\" },\n  { name = \"enums3\", path = \"../exercises/08_enums/enums3.rs\" },\n  { name = \"enums3_sol\", path = \"../solutions/08_enums/enums3.rs\" },\n  { name = \"strings1\", path = \"../exercises/09_strings/strings1.rs\" },\n  { name = \"strings1_sol\", path = \"../solutions/09_strings/strings1.rs\" },\n  { name = \"strings2\", path = \"../exercises/09_strings/strings2.rs\" },\n  { name = \"strings2_sol\", path = \"../solutions/09_strings/strings2.rs\" },\n  { name = \"strings3\", path = \"../exercises/09_strings/strings3.rs\" },\n  { name = \"strings3_sol\", path = \"../solutions/09_strings/strings3.rs\" },\n  { name = \"strings4\", path = \"../exercises/09_strings/strings4.rs\" },\n  { name = \"strings4_sol\", path = \"../solutions/09_strings/strings4.rs\" },\n  { name = \"modules1\", path = \"../exercises/10_modules/modules1.rs\" },\n  { name = \"modules1_sol\", path = \"../solutions/10_modules/modules1.rs\" },\n  { name = \"modules2\", path = \"../exercises/10_modules/modules2.rs\" },\n  { name = \"modules2_sol\", path = \"../solutions/10_modules/modules2.rs\" },\n  { name = \"modules3\", path = \"../exercises/10_modules/modules3.rs\" },\n  { name = \"modules3_sol\", path = \"../solutions/10_modules/modules3.rs\" },\n  { name = \"hashmaps1\", path = \"../exercises/11_hashmaps/hashmaps1.rs\" },\n  { name = \"hashmaps1_sol\", path = \"../solutions/11_hashmaps/hashmaps1.rs\" },\n  { name = \"hashmaps2\", path = \"../exercises/11_hashmaps/hashmaps2.rs\" },\n  { name = \"hashmaps2_sol\", path = \"../solutions/11_hashmaps/hashmaps2.rs\" },\n  { name = \"hashmaps3\", path = \"../exercises/11_hashmaps/hashmaps3.rs\" },\n  { name = \"hashmaps3_sol\", path = \"../solutions/11_hashmaps/hashmaps3.rs\" },\n  { name = \"quiz2\", path = \"../exercises/quizzes/quiz2.rs\" },\n  { name = \"quiz2_sol\", path = \"../solutions/quizzes/quiz2.rs\" },\n  { name = \"options1\", path = \"../exercises/12_options/options1.rs\" },\n  { name = \"options1_sol\", path = \"../solutions/12_options/options1.rs\" },\n  { name = \"options2\", path = \"../exercises/12_options/options2.rs\" },\n  { name = \"options2_sol\", path = \"../solutions/12_options/options2.rs\" },\n  { name = \"options3\", path = \"../exercises/12_options/options3.rs\" },\n  { name = \"options3_sol\", path = \"../solutions/12_options/options3.rs\" },\n  { name = \"errors1\", path = \"../exercises/13_error_handling/errors1.rs\" },\n  { name = \"errors1_sol\", path = \"../solutions/13_error_handling/errors1.rs\" },\n  { name = \"errors2\", path = \"../exercises/13_error_handling/errors2.rs\" },\n  { name = \"errors2_sol\", path = \"../solutions/13_error_handling/errors2.rs\" },\n  { name = \"errors3\", path = \"../exercises/13_error_handling/errors3.rs\" },\n  { name = \"errors3_sol\", path = \"../solutions/13_error_handling/errors3.rs\" },\n  { name = \"errors4\", path = \"../exercises/13_error_handling/errors4.rs\" },\n  { name = \"errors4_sol\", path = \"../solutions/13_error_handling/errors4.rs\" },\n  { name = \"errors5\", path = \"../exercises/13_error_handling/errors5.rs\" },\n  { name = \"errors5_sol\", path = \"../solutions/13_error_handling/errors5.rs\" },\n  { name = \"errors6\", path = \"../exercises/13_error_handling/errors6.rs\" },\n  { name = \"errors6_sol\", path = \"../solutions/13_error_handling/errors6.rs\" },\n  { name = \"generics1\", path = \"../exercises/14_generics/generics1.rs\" },\n  { name = \"generics1_sol\", path = \"../solutions/14_generics/generics1.rs\" },\n  { name = \"generics2\", path = \"../exercises/14_generics/generics2.rs\" },\n  { name = \"generics2_sol\", path = \"../solutions/14_generics/generics2.rs\" },\n  { name = \"traits1\", path = \"../exercises/15_traits/traits1.rs\" },\n  { name = \"traits1_sol\", path = \"../solutions/15_traits/traits1.rs\" },\n  { name = \"traits2\", path = \"../exercises/15_traits/traits2.rs\" },\n  { name = \"traits2_sol\", path = \"../solutions/15_traits/traits2.rs\" },\n  { name = \"traits3\", path = \"../exercises/15_traits/traits3.rs\" },\n  { name = \"traits3_sol\", path = \"../solutions/15_traits/traits3.rs\" },\n  { name = \"traits4\", path = \"../exercises/15_traits/traits4.rs\" },\n  { name = \"traits4_sol\", path = \"../solutions/15_traits/traits4.rs\" },\n  { name = \"traits5\", path = \"../exercises/15_traits/traits5.rs\" },\n  { name = \"traits5_sol\", path = \"../solutions/15_traits/traits5.rs\" },\n  { name = \"quiz3\", path = \"../exercises/quizzes/quiz3.rs\" },\n  { name = \"quiz3_sol\", path = \"../solutions/quizzes/quiz3.rs\" },\n  { name = \"lifetimes1\", path = \"../exercises/16_lifetimes/lifetimes1.rs\" },\n  { name = \"lifetimes1_sol\", path = \"../solutions/16_lifetimes/lifetimes1.rs\" },\n  { name = \"lifetimes2\", path = \"../exercises/16_lifetimes/lifetimes2.rs\" },\n  { name = \"lifetimes2_sol\", path = \"../solutions/16_lifetimes/lifetimes2.rs\" },\n  { name = \"lifetimes3\", path = \"../exercises/16_lifetimes/lifetimes3.rs\" },\n  { name = \"lifetimes3_sol\", path = \"../solutions/16_lifetimes/lifetimes3.rs\" },\n  { name = \"tests1\", path = \"../exercises/17_tests/tests1.rs\" },\n  { name = \"tests1_sol\", path = \"../solutions/17_tests/tests1.rs\" },\n  { name = \"tests2\", path = \"../exercises/17_tests/tests2.rs\" },\n  { name = \"tests2_sol\", path = \"../solutions/17_tests/tests2.rs\" },\n  { name = \"tests3\", path = \"../exercises/17_tests/tests3.rs\" },\n  { name = \"tests3_sol\", path = \"../solutions/17_tests/tests3.rs\" },\n  { name = \"iterators1\", path = \"../exercises/18_iterators/iterators1.rs\" },\n  { name = \"iterators1_sol\", path = \"../solutions/18_iterators/iterators1.rs\" },\n  { name = \"iterators2\", path = \"../exercises/18_iterators/iterators2.rs\" },\n  { name = \"iterators2_sol\", path = \"../solutions/18_iterators/iterators2.rs\" },\n  { name = \"iterators3\", path = \"../exercises/18_iterators/iterators3.rs\" },\n  { name = \"iterators3_sol\", path = \"../solutions/18_iterators/iterators3.rs\" },\n  { name = \"iterators4\", path = \"../exercises/18_iterators/iterators4.rs\" },\n  { name = \"iterators4_sol\", path = \"../solutions/18_iterators/iterators4.rs\" },\n  { name = \"iterators5\", path = \"../exercises/18_iterators/iterators5.rs\" },\n  { name = \"iterators5_sol\", path = \"../solutions/18_iterators/iterators5.rs\" },\n  { name = \"box1\", path = \"../exercises/19_smart_pointers/box1.rs\" },\n  { name = \"box1_sol\", path = \"../solutions/19_smart_pointers/box1.rs\" },\n  { name = \"rc1\", path = \"../exercises/19_smart_pointers/rc1.rs\" },\n  { name = \"rc1_sol\", path = \"../solutions/19_smart_pointers/rc1.rs\" },\n  { name = \"arc1\", path = \"../exercises/19_smart_pointers/arc1.rs\" },\n  { name = \"arc1_sol\", path = \"../solutions/19_smart_pointers/arc1.rs\" },\n  { name = \"cow1\", path = \"../exercises/19_smart_pointers/cow1.rs\" },\n  { name = \"cow1_sol\", path = \"../solutions/19_smart_pointers/cow1.rs\" },\n  { name = \"threads1\", path = \"../exercises/20_threads/threads1.rs\" },\n  { name = \"threads1_sol\", path = \"../solutions/20_threads/threads1.rs\" },\n  { name = \"threads2\", path = \"../exercises/20_threads/threads2.rs\" },\n  { name = \"threads2_sol\", path = \"../solutions/20_threads/threads2.rs\" },\n  { name = \"threads3\", path = \"../exercises/20_threads/threads3.rs\" },\n  { name = \"threads3_sol\", path = \"../solutions/20_threads/threads3.rs\" },\n  { name = \"macros1\", path = \"../exercises/21_macros/macros1.rs\" },\n  { name = \"macros1_sol\", path = \"../solutions/21_macros/macros1.rs\" },\n  { name = \"macros2\", path = \"../exercises/21_macros/macros2.rs\" },\n  { name = \"macros2_sol\", path = \"../solutions/21_macros/macros2.rs\" },\n  { name = \"macros3\", path = \"../exercises/21_macros/macros3.rs\" },\n  { name = \"macros3_sol\", path = \"../solutions/21_macros/macros3.rs\" },\n  { name = \"macros4\", path = \"../exercises/21_macros/macros4.rs\" },\n  { name = \"macros4_sol\", path = \"../solutions/21_macros/macros4.rs\" },\n  { name = \"clippy1\", path = \"../exercises/22_clippy/clippy1.rs\" },\n  { name = \"clippy1_sol\", path = \"../solutions/22_clippy/clippy1.rs\" },\n  { name = \"clippy2\", path = \"../exercises/22_clippy/clippy2.rs\" },\n  { name = \"clippy2_sol\", path = \"../solutions/22_clippy/clippy2.rs\" },\n  { name = \"clippy3\", path = \"../exercises/22_clippy/clippy3.rs\" },\n  { name = \"clippy3_sol\", path = \"../solutions/22_clippy/clippy3.rs\" },\n  { name = \"using_as\", path = \"../exercises/23_conversions/using_as.rs\" },\n  { name = \"using_as_sol\", path = \"../solutions/23_conversions/using_as.rs\" },\n  { name = \"from_into\", path = \"../exercises/23_conversions/from_into.rs\" },\n  { name = \"from_into_sol\", path = \"../solutions/23_conversions/from_into.rs\" },\n  { name = \"from_str\", path = \"../exercises/23_conversions/from_str.rs\" },\n  { name = \"from_str_sol\", path = \"../solutions/23_conversions/from_str.rs\" },\n  { name = \"try_from_into\", path = \"../exercises/23_conversions/try_from_into.rs\" },\n  { name = \"try_from_into_sol\", path = \"../solutions/23_conversions/try_from_into.rs\" },\n  { name = \"as_ref_mut\", path = \"../exercises/23_conversions/as_ref_mut.rs\" },\n  { name = \"as_ref_mut_sol\", path = \"../solutions/23_conversions/as_ref_mut.rs\" },\n]\n\n[package]\nname = \"exercises\"\nedition = \"2024\"\n# Don't publish the exercises on crates.io!\npublish = false\n\n[profile.release]\npanic = \"abort\"\n\n[profile.dev]\npanic = \"abort\"\n\n[lints.rust]\n# You shouldn't write unsafe code in Rustlings!\nunsafe_code = \"forbid\"\n# You don't need unstable features in Rustlings and shouldn't rely on them while learning Rust.\nunstable_features = \"forbid\"\n# Dead code warnings can't be avoided in some exercises and might distract while learning.\ndead_code = \"allow\"\n\n[lints.clippy]\n# You forgot a `todo!()`!\ntodo = \"forbid\"\n# This can only happen by mistake in Rustlings.\nempty_loop = \"forbid\"\n# No infinite loops are needed in Rustlings.\ninfinite_loop = \"deny\"\n# You shouldn't leak memory while still learning Rust!\nmem_forget = \"deny\"\n# Currently, there are no disallowed methods. This line avoids problems when developing Rustlings.\ndisallowed_methods = \"allow\"\n"
  },
  {
    "path": "dev/rustlings-repo.txt",
    "content": "This file is used to check if the user tries to run Rustlings in the repository (the method before version 6)\n"
  },
  {
    "path": "exercises/00_intro/README.md",
    "content": "# Intro\n\nRust uses the `print!` and `println!` macros to print text to the console.\n\n## Further information\n\n- [Hello World](https://doc.rust-lang.org/rust-by-example/hello.html)\n- [Formatted print](https://doc.rust-lang.org/rust-by-example/hello/print.html)\n"
  },
  {
    "path": "exercises/00_intro/intro1.rs",
    "content": "// TODO: We sometimes encourage you to keep trying things on a given exercise\n// even after you already figured it out. If you got everything working and feel\n// ready for the next exercise, enter `n` in the terminal.\n//\n// The exercise file will be reloaded when you change one of the lines below!\n// Try adding a new `println!` and check the updated output in the terminal.\n\nfn main() {\n    println!(r#\"       Welcome to...                      \"#);\n    println!(r#\"                 _   _ _                  \"#);\n    println!(r#\"  _ __ _   _ ___| |_| (_)_ __   __ _ ___  \"#);\n    println!(r#\" | '__| | | / __| __| | | '_ \\ / _` / __| \"#);\n    println!(r#\" | |  | |_| \\__ \\ |_| | | | | | (_| \\__ \\ \"#);\n    println!(r#\" |_|   \\__,_|___/\\__|_|_|_| |_|\\__, |___/ \"#);\n    println!(r#\"                               |___/      \"#);\n    println!();\n    println!(\"This exercise compiles successfully. The remaining exercises contain a compiler\");\n    println!(\"or logic error. The central concept behind Rustlings is to fix these errors and\");\n    println!(\"solve the exercises. Good luck!\");\n    println!();\n    println!(\"The file of this exercise is `exercises/00_intro/intro1.rs`. Have a look!\");\n    println!(\"The current exercise path will be always shown under the progress bar.\");\n    println!(\"You can click on the path to open the exercise file in your editor.\");\n}\n"
  },
  {
    "path": "exercises/00_intro/intro2.rs",
    "content": "fn main() {\n    // TODO: Fix the code to print \"Hello world!\".\n    printline!(\"Hello world!\");\n}\n"
  },
  {
    "path": "exercises/01_variables/README.md",
    "content": "# Variables\n\nIn Rust, variables are immutable by default.\nWhen a variable is immutable, once a value is bound to a name, you can't change that value.\nYou can make them mutable by adding `mut` in front of the variable name.\n\n## Further information\n\n- [Variables and Mutability](https://doc.rust-lang.org/book/ch03-01-variables-and-mutability.html)\n"
  },
  {
    "path": "exercises/01_variables/variables1.rs",
    "content": "fn main() {\n    // TODO: Add the missing keyword.\n    x = 5;\n\n    println!(\"x has the value {x}\");\n}\n"
  },
  {
    "path": "exercises/01_variables/variables2.rs",
    "content": "fn main() {\n    // TODO: Change the line below to fix the compiler error.\n    let x;\n\n    if x == 10 {\n        println!(\"x is ten!\");\n    } else {\n        println!(\"x is not ten!\");\n    }\n}\n"
  },
  {
    "path": "exercises/01_variables/variables3.rs",
    "content": "fn main() {\n    // TODO: Change the line below to fix the compiler error.\n    let x: i32;\n\n    println!(\"Number {x}\");\n}\n"
  },
  {
    "path": "exercises/01_variables/variables4.rs",
    "content": "// TODO: Fix the compiler error.\nfn main() {\n    let x = 3;\n    println!(\"Number {x}\");\n\n    x = 5; // Don't change this line\n    println!(\"Number {x}\");\n}\n"
  },
  {
    "path": "exercises/01_variables/variables5.rs",
    "content": "fn main() {\n    let number = \"T-H-R-E-E\"; // Don't change this line\n    println!(\"Spell a number: {number}\");\n\n    // TODO: Fix the compiler error by changing the line below without renaming the variable.\n    number = 3;\n    println!(\"Number plus two is: {}\", number + 2);\n}\n"
  },
  {
    "path": "exercises/01_variables/variables6.rs",
    "content": "// TODO: Change the line below to fix the compiler error.\nconst NUMBER = 3;\n\nfn main() {\n    println!(\"Number: {NUMBER}\");\n}\n"
  },
  {
    "path": "exercises/02_functions/README.md",
    "content": "# Functions\n\nHere, you'll learn how to write functions and how the Rust compiler can help you debug errors even\nin more complex code.\n\n## Further information\n\n- [How Functions Work](https://doc.rust-lang.org/book/ch03-03-how-functions-work.html)\n"
  },
  {
    "path": "exercises/02_functions/functions1.rs",
    "content": "// TODO: Add some function with the name `call_me` without arguments or a return value.\n\nfn main() {\n    call_me(); // Don't change this line\n}\n"
  },
  {
    "path": "exercises/02_functions/functions2.rs",
    "content": "// TODO: Add the missing type of the argument `num` after the colon `:`.\nfn call_me(num:) {\n    for i in 0..num {\n        println!(\"Ring! Call number {}\", i + 1);\n    }\n}\n\nfn main() {\n    call_me(3);\n}\n"
  },
  {
    "path": "exercises/02_functions/functions3.rs",
    "content": "fn call_me(num: u8) {\n    for i in 0..num {\n        println!(\"Ring! Call number {}\", i + 1);\n    }\n}\n\nfn main() {\n    // TODO: Fix the function call.\n    call_me();\n}\n"
  },
  {
    "path": "exercises/02_functions/functions4.rs",
    "content": "// This store is having a sale where if the price is an even number, you get 10\n// Rustbucks off, but if it's an odd number, it's 3 Rustbucks off.\n// Don't worry about the function bodies themselves, we are only interested in\n// the signatures for now.\n\nfn is_even(num: i64) -> bool {\n    num % 2 == 0\n}\n\n// TODO: Fix the function signature.\nfn sale_price(price: i64) -> {\n    if is_even(price) {\n        price - 10\n    } else {\n        price - 3\n    }\n}\n\nfn main() {\n    let original_price = 51;\n    println!(\"Your sale price is {}\", sale_price(original_price));\n}\n"
  },
  {
    "path": "exercises/02_functions/functions5.rs",
    "content": "// TODO: Fix the function body without changing the signature.\nfn square(num: i32) -> i32 {\n    num * num;\n}\n\nfn main() {\n    let answer = square(3);\n    println!(\"The square of 3 is {answer}\");\n}\n"
  },
  {
    "path": "exercises/03_if/README.md",
    "content": "# If\n\n`if`, the most basic (but still surprisingly versatile!) type of control flow, is what you'll learn here.\n\n## Further information\n\n- [Control Flow - if expressions](https://doc.rust-lang.org/book/ch03-05-control-flow.html#if-expressions)\n"
  },
  {
    "path": "exercises/03_if/if1.rs",
    "content": "fn bigger(a: i32, b: i32) -> i32 {\n    // TODO: Complete this function to return the bigger number!\n    // If both numbers are equal, any of them can be returned.\n    // Do not use:\n    // - another function call\n    // - additional variables\n}\n\nfn main() {\n    // You can optionally experiment here.\n}\n\n// Don't mind this for now :)\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    #[test]\n    fn ten_is_bigger_than_eight() {\n        assert_eq!(10, bigger(10, 8));\n    }\n\n    #[test]\n    fn fortytwo_is_bigger_than_thirtytwo() {\n        assert_eq!(42, bigger(32, 42));\n    }\n\n    #[test]\n    fn equal_numbers() {\n        assert_eq!(42, bigger(42, 42));\n    }\n}\n"
  },
  {
    "path": "exercises/03_if/if2.rs",
    "content": "// TODO: Fix the compiler error on this function.\nfn picky_eater(food: &str) -> &str {\n    if food == \"strawberry\" {\n        \"Yummy!\"\n    } else {\n        1\n    }\n}\n\nfn main() {\n    // You can optionally experiment here.\n}\n\n// TODO: Read the tests to understand the desired behavior.\n// Make all tests pass without changing them.\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    #[test]\n    fn yummy_food() {\n        // This means that calling `picky_eater` with the argument \"strawberry\" should return \"Yummy!\".\n        assert_eq!(picky_eater(\"strawberry\"), \"Yummy!\");\n    }\n\n    #[test]\n    fn neutral_food() {\n        assert_eq!(picky_eater(\"potato\"), \"I guess I can eat that.\");\n    }\n\n    #[test]\n    fn default_disliked_food() {\n        assert_eq!(picky_eater(\"broccoli\"), \"No thanks!\");\n        assert_eq!(picky_eater(\"gummy bears\"), \"No thanks!\");\n        assert_eq!(picky_eater(\"literally anything\"), \"No thanks!\");\n    }\n}\n"
  },
  {
    "path": "exercises/03_if/if3.rs",
    "content": "fn animal_habitat(animal: &str) -> &str {\n    // TODO: Fix the compiler error in the statement below.\n    let identifier = if animal == \"crab\" {\n        1\n    } else if animal == \"gopher\" {\n        2.0\n    } else if animal == \"snake\" {\n        3\n    } else {\n        \"Unknown\"\n    };\n\n    // Don't change the expression below!\n    if identifier == 1 {\n        \"Beach\"\n    } else if identifier == 2 {\n        \"Burrow\"\n    } else if identifier == 3 {\n        \"Desert\"\n    } else {\n        \"Unknown\"\n    }\n}\n\nfn main() {\n    // You can optionally experiment here.\n}\n\n// Don't change the tests!\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    #[test]\n    fn gopher_lives_in_burrow() {\n        assert_eq!(animal_habitat(\"gopher\"), \"Burrow\")\n    }\n\n    #[test]\n    fn snake_lives_in_desert() {\n        assert_eq!(animal_habitat(\"snake\"), \"Desert\")\n    }\n\n    #[test]\n    fn crab_lives_on_beach() {\n        assert_eq!(animal_habitat(\"crab\"), \"Beach\")\n    }\n\n    #[test]\n    fn unknown_animal() {\n        assert_eq!(animal_habitat(\"dinosaur\"), \"Unknown\")\n    }\n}\n"
  },
  {
    "path": "exercises/04_primitive_types/README.md",
    "content": "# Primitive Types\n\nRust has a couple of basic types that are directly implemented into the\ncompiler. In this section, we'll go through the most important ones.\n\n## Further information\n\n- [Data Types](https://doc.rust-lang.org/book/ch03-02-data-types.html)\n- [The Slice Type](https://doc.rust-lang.org/book/ch04-03-slices.html)\n"
  },
  {
    "path": "exercises/04_primitive_types/primitive_types1.rs",
    "content": "// Booleans (`bool`)\n\nfn main() {\n    let is_morning = true;\n    if is_morning {\n        println!(\"Good morning!\");\n    }\n\n    // TODO: Define a boolean variable with the name `is_evening` before the `if` statement below.\n    // The value of the variable should be the negation (opposite) of `is_morning`.\n    // let …\n    if is_evening {\n        println!(\"Good evening!\");\n    }\n}\n"
  },
  {
    "path": "exercises/04_primitive_types/primitive_types2.rs",
    "content": "// Characters (`char`)\n\nfn main() {\n    // Note the _single_ quotes, these are different from the double quotes\n    // you've been seeing around.\n    let my_first_initial = 'C';\n    if my_first_initial.is_alphabetic() {\n        println!(\"Alphabetical!\");\n    } else if my_first_initial.is_numeric() {\n        println!(\"Numerical!\");\n    } else {\n        println!(\"Neither alphabetic nor numeric!\");\n    }\n\n    // TODO: Analogous to the example before, declare a variable called `your_character`\n    // below with your favorite character.\n    // Try a letter, try a digit (in single quotes), try a special character, try a character\n    // from a different language than your own, try an emoji 😉\n    // let your_character = '';\n\n    if your_character.is_alphabetic() {\n        println!(\"Alphabetical!\");\n    } else if your_character.is_numeric() {\n        println!(\"Numerical!\");\n    } else {\n        println!(\"Neither alphabetic nor numeric!\");\n    }\n}\n"
  },
  {
    "path": "exercises/04_primitive_types/primitive_types3.rs",
    "content": "fn main() {\n    // TODO: Create an array called `a` with at least 100 elements in it.\n    // let a = ???\n\n    if a.len() >= 100 {\n        println!(\"Wow, that's a big array!\");\n    } else {\n        println!(\"Meh, I eat arrays like that for breakfast.\");\n        panic!(\"Array not big enough, more elements needed\");\n    }\n}\n"
  },
  {
    "path": "exercises/04_primitive_types/primitive_types4.rs",
    "content": "fn main() {\n    // You can optionally experiment here.\n}\n\n#[cfg(test)]\nmod tests {\n    #[test]\n    fn slice_out_of_array() {\n        let a = [1, 2, 3, 4, 5];\n\n        // TODO: Get a slice called `nice_slice` out of the array `a` so that the test passes.\n        // let nice_slice = ???\n\n        assert_eq!([2, 3, 4], nice_slice);\n    }\n}\n"
  },
  {
    "path": "exercises/04_primitive_types/primitive_types5.rs",
    "content": "fn main() {\n    let cat = (\"Furry McFurson\", 3.5);\n\n    // TODO: Destructure the `cat` tuple in one statement so that the println works.\n    // let /* your pattern here */ = cat;\n\n    println!(\"{name} is {age} years old\");\n}\n"
  },
  {
    "path": "exercises/04_primitive_types/primitive_types6.rs",
    "content": "fn main() {\n    // You can optionally experiment here.\n}\n\n#[cfg(test)]\nmod tests {\n    #[test]\n    fn indexing_tuple() {\n        let numbers = (1, 2, 3);\n\n        // TODO: Use a tuple index to access the second element of `numbers`\n        // and assign it to a variable called `second`.\n        // let second = ???;\n\n        assert_eq!(second, 2, \"This is not the 2nd number in the tuple!\");\n    }\n}\n"
  },
  {
    "path": "exercises/05_vecs/README.md",
    "content": "# Vectors\n\nVectors are one of the most-used Rust data structures. In other programming\nlanguages, they'd simply be called Arrays, but since Rust operates on a\nbit of a lower level, an array in Rust is stored on the stack (meaning it\ncan't grow or shrink, and the size needs to be known at compile time),\nand a Vector is stored in the heap (where these restrictions do not apply).\n\nVectors are a bit of a later chapter in the book, but we think that they're\nuseful enough to talk about them a bit earlier. We shall be talking about\nthe other useful data structure, hash maps, later.\n\n## Further information\n\n- [Storing Lists of Values with Vectors](https://doc.rust-lang.org/book/ch08-01-vectors.html)\n- [`iter_mut`](https://doc.rust-lang.org/std/primitive.slice.html#method.iter_mut)\n- [`map`](https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.map)\n"
  },
  {
    "path": "exercises/05_vecs/vecs1.rs",
    "content": "fn array_and_vec() -> ([i32; 4], Vec<i32>) {\n    let a = [10, 20, 30, 40]; // Array\n\n    // TODO: Create a vector called `v` which contains the exact same elements as in the array `a`.\n    // Use the vector macro.\n    // let v = ???;\n\n    (a, v)\n}\n\nfn main() {\n    // You can optionally experiment here.\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    #[test]\n    fn test_array_and_vec_similarity() {\n        let (a, v) = array_and_vec();\n        assert_eq!(a, *v);\n    }\n}\n"
  },
  {
    "path": "exercises/05_vecs/vecs2.rs",
    "content": "fn vec_loop(input: &[i32]) -> Vec<i32> {\n    let mut output = Vec::new();\n\n    for element in input {\n        // TODO: Multiply each element in the `input` slice by 2 and push it to\n        // the `output` vector.\n    }\n\n    output\n}\n\nfn main() {\n    // You can optionally experiment here.\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    #[test]\n    fn test_vec_loop() {\n        let input = [2, 4, 6, 8, 10];\n        let ans = vec_loop(&input);\n        assert_eq!(ans, [4, 8, 12, 16, 20]);\n    }\n}\n"
  },
  {
    "path": "exercises/06_move_semantics/README.md",
    "content": "# Move Semantics\n\nThese exercises are adapted from [pnkfelix](https://github.com/pnkfelix)'s [Rust Tutorial](https://pnkfelix.github.io/rust-examples-icfp2014/) -- Thank you Felix!!!\n\n## Further information\n\nFor this section, the book links are especially important.\n\n- [Ownership](https://doc.rust-lang.org/book/ch04-01-what-is-ownership.html)\n- [Reference and borrowing](https://doc.rust-lang.org/book/ch04-02-references-and-borrowing.html)\n"
  },
  {
    "path": "exercises/06_move_semantics/move_semantics1.rs",
    "content": "// TODO: Fix the compiler error in this function.\nfn fill_vec(vec: Vec<i32>) -> Vec<i32> {\n    let vec = vec;\n\n    vec.push(88);\n\n    vec\n}\n\nfn main() {\n    // You can optionally experiment here.\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    #[test]\n    fn move_semantics1() {\n        let vec0 = vec![22, 44, 66];\n        let vec1 = fill_vec(vec0);\n        assert_eq!(vec1, vec![22, 44, 66, 88]);\n    }\n}\n"
  },
  {
    "path": "exercises/06_move_semantics/move_semantics2.rs",
    "content": "fn fill_vec(vec: Vec<i32>) -> Vec<i32> {\n    let mut vec = vec;\n\n    vec.push(88);\n\n    vec\n}\n\nfn main() {\n    // You can optionally experiment here.\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    // TODO: Make both vectors `vec0` and `vec1` accessible at the same time to\n    // fix the compiler error in the test.\n    #[test]\n    fn move_semantics2() {\n        let vec0 = vec![22, 44, 66];\n\n        let vec1 = fill_vec(vec0);\n\n        assert_eq!(vec0, [22, 44, 66]);\n        assert_eq!(vec1, [22, 44, 66, 88]);\n    }\n}\n"
  },
  {
    "path": "exercises/06_move_semantics/move_semantics3.rs",
    "content": "// TODO: Fix the compiler error in the function without adding any new line.\nfn fill_vec(vec: Vec<i32>) -> Vec<i32> {\n    vec.push(88);\n\n    vec\n}\n\nfn main() {\n    // You can optionally experiment here.\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    #[test]\n    fn move_semantics3() {\n        let vec0 = vec![22, 44, 66];\n        let vec1 = fill_vec(vec0);\n        assert_eq!(vec1, [22, 44, 66, 88]);\n    }\n}\n"
  },
  {
    "path": "exercises/06_move_semantics/move_semantics4.rs",
    "content": "fn main() {\n    // You can optionally experiment here.\n}\n\n#[cfg(test)]\nmod tests {\n    // TODO: Fix the compiler errors only by reordering the lines in the test.\n    // Don't add, change or remove any line.\n    #[test]\n    fn move_semantics4() {\n        let mut x = Vec::new();\n        let y = &mut x;\n        let z = &mut x;\n        y.push(42);\n        z.push(13);\n        assert_eq!(x, [42, 13]);\n    }\n}\n"
  },
  {
    "path": "exercises/06_move_semantics/move_semantics5.rs",
    "content": "#![allow(clippy::ptr_arg)]\n\n// TODO: Fix the compiler errors without changing anything except adding or\n// removing references (the character `&`).\n\n// Shouldn't take ownership\nfn get_char(data: String) -> char {\n    data.chars().last().unwrap()\n}\n\n// Should take ownership\nfn string_uppercase(mut data: &String) {\n    data = data.to_uppercase();\n\n    println!(\"{data}\");\n}\n\nfn main() {\n    let data = \"Rust is great!\".to_string();\n\n    get_char(data);\n\n    string_uppercase(&data);\n}\n"
  },
  {
    "path": "exercises/07_structs/README.md",
    "content": "# Structs\n\nRust has three struct types: a classic C struct, a tuple struct, and a unit struct.\n\n## Further information\n\n- [Structures](https://doc.rust-lang.org/book/ch05-01-defining-structs.html)\n- [Method Syntax](https://doc.rust-lang.org/book/ch05-03-method-syntax.html)\n"
  },
  {
    "path": "exercises/07_structs/structs1.rs",
    "content": "struct ColorRegularStruct {\n    // TODO: Add the fields that the test `regular_structs` expects.\n    // What types should the fields have? What are the minimum and maximum values for RGB colors?\n}\n\nstruct ColorTupleStruct(/* TODO: Add the fields that the test `tuple_structs` expects */);\n\n#[derive(Debug)]\nstruct UnitStruct;\n\nfn main() {\n    // You can optionally experiment here.\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    #[test]\n    fn regular_structs() {\n        // TODO: Instantiate a regular struct.\n        // let green =\n\n        assert_eq!(green.red, 0);\n        assert_eq!(green.green, 255);\n        assert_eq!(green.blue, 0);\n    }\n\n    #[test]\n    fn tuple_structs() {\n        // TODO: Instantiate a tuple struct.\n        // let green =\n\n        assert_eq!(green.0, 0);\n        assert_eq!(green.1, 255);\n        assert_eq!(green.2, 0);\n    }\n\n    #[test]\n    fn unit_structs() {\n        // TODO: Instantiate a unit struct.\n        // let unit_struct =\n        let message = format!(\"{unit_struct:?}s are fun!\");\n\n        assert_eq!(message, \"UnitStructs are fun!\");\n    }\n}\n"
  },
  {
    "path": "exercises/07_structs/structs2.rs",
    "content": "#[derive(Debug)]\nstruct Order {\n    name: String,\n    year: u32,\n    made_by_phone: bool,\n    made_by_mobile: bool,\n    made_by_email: bool,\n    item_number: u32,\n    count: u32,\n}\n\nfn create_order_template() -> Order {\n    Order {\n        name: String::from(\"Bob\"),\n        year: 2019,\n        made_by_phone: false,\n        made_by_mobile: false,\n        made_by_email: true,\n        item_number: 123,\n        count: 0,\n    }\n}\n\nfn main() {\n    // You can optionally experiment here.\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    #[test]\n    fn your_order() {\n        let order_template = create_order_template();\n\n        // TODO: Create your own order using the update syntax and template above!\n        // let your_order =\n\n        assert_eq!(your_order.name, \"Hacker in Rust\");\n        assert_eq!(your_order.year, order_template.year);\n        assert_eq!(your_order.made_by_phone, order_template.made_by_phone);\n        assert_eq!(your_order.made_by_mobile, order_template.made_by_mobile);\n        assert_eq!(your_order.made_by_email, order_template.made_by_email);\n        assert_eq!(your_order.item_number, order_template.item_number);\n        assert_eq!(your_order.count, 1);\n    }\n}\n"
  },
  {
    "path": "exercises/07_structs/structs3.rs",
    "content": "// Structs contain data, but can also have logic. In this exercise, we have\n// defined the `Package` struct, and we want to test some logic attached to it.\n\n#[derive(Debug)]\nstruct Package {\n    sender_country: String,\n    recipient_country: String,\n    weight_in_grams: u32,\n}\n\nimpl Package {\n    fn new(sender_country: String, recipient_country: String, weight_in_grams: u32) -> Self {\n        if weight_in_grams < 10 {\n            // This isn't how you should handle errors in Rust, but we will\n            // learn about error handling later.\n            panic!(\"Can't ship a package with weight below 10 grams\");\n        }\n\n        Self {\n            sender_country,\n            recipient_country,\n            weight_in_grams,\n        }\n    }\n\n    // TODO: Add the correct return type to the function signature.\n    fn is_international(&self) {\n        // TODO: Read the tests that use this method to find out when a package\n        // is considered international.\n    }\n\n    // TODO: Add the correct return type to the function signature.\n    fn get_fees(&self, cents_per_gram: u32) {\n        // TODO: Calculate the package's fees.\n    }\n}\n\nfn main() {\n    // You can optionally experiment here.\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    #[test]\n    #[should_panic]\n    fn fail_creating_weightless_package() {\n        let sender_country = String::from(\"Spain\");\n        let recipient_country = String::from(\"Austria\");\n\n        Package::new(sender_country, recipient_country, 5);\n    }\n\n    #[test]\n    fn create_international_package() {\n        let sender_country = String::from(\"Spain\");\n        let recipient_country = String::from(\"Russia\");\n\n        let package = Package::new(sender_country, recipient_country, 1200);\n\n        assert!(package.is_international());\n    }\n\n    #[test]\n    fn create_local_package() {\n        let sender_country = String::from(\"Canada\");\n        let recipient_country = sender_country.clone();\n\n        let package = Package::new(sender_country, recipient_country, 1200);\n\n        assert!(!package.is_international());\n    }\n\n    #[test]\n    fn calculate_transport_fees() {\n        let sender_country = String::from(\"Spain\");\n        let recipient_country = String::from(\"Spain\");\n\n        let cents_per_gram = 3;\n\n        let package = Package::new(sender_country, recipient_country, 1500);\n\n        assert_eq!(package.get_fees(cents_per_gram), 4500);\n        assert_eq!(package.get_fees(cents_per_gram * 2), 9000);\n    }\n}\n"
  },
  {
    "path": "exercises/08_enums/README.md",
    "content": "# Enums\n\nRust allows you to define types called \"enums\" which enumerate possible values.\nEnums are a feature in many languages, but their capabilities differ in each language. Rust's enums are most similar to algebraic data types in functional languages, such as F#, OCaml, and Haskell.\nUseful in combination with enums is Rust's \"pattern matching\" facility, which makes it easy to run different code for different values of an enumeration.\n\n## Further information\n\n- [Enums](https://doc.rust-lang.org/book/ch06-00-enums.html)\n- [Pattern syntax](https://doc.rust-lang.org/book/ch19-03-pattern-syntax.html)\n"
  },
  {
    "path": "exercises/08_enums/enums1.rs",
    "content": "#[derive(Debug)]\nenum Message {\n    // TODO: Define a few types of messages as used below.\n}\n\nfn main() {\n    println!(\"{:?}\", Message::Resize);\n    println!(\"{:?}\", Message::Move);\n    println!(\"{:?}\", Message::Echo);\n    println!(\"{:?}\", Message::ChangeColor);\n    println!(\"{:?}\", Message::Quit);\n}\n"
  },
  {
    "path": "exercises/08_enums/enums2.rs",
    "content": "#[derive(Debug)]\nstruct Point {\n    x: u64,\n    y: u64,\n}\n\n#[derive(Debug)]\nenum Message {\n    // TODO: Define the different variants used below.\n}\n\nimpl Message {\n    fn call(&self) {\n        println!(\"{self:?}\");\n    }\n}\n\nfn main() {\n    let messages = [\n        Message::Resize {\n            width: 10,\n            height: 30,\n        },\n        Message::Move(Point { x: 10, y: 15 }),\n        Message::Echo(String::from(\"hello world\")),\n        Message::ChangeColor(200, 255, 255),\n        Message::Quit,\n    ];\n\n    for message in &messages {\n        message.call();\n    }\n}\n"
  },
  {
    "path": "exercises/08_enums/enums3.rs",
    "content": "struct Point {\n    x: u64,\n    y: u64,\n}\n\nenum Message {\n    Resize { width: u64, height: u64 },\n    Move(Point),\n    Echo(String),\n    ChangeColor(u8, u8, u8),\n    Quit,\n}\n\nstruct State {\n    width: u64,\n    height: u64,\n    position: Point,\n    message: String,\n    // RGB color composed of red, green and blue.\n    color: (u8, u8, u8),\n    quit: bool,\n}\n\nimpl State {\n    fn resize(&mut self, width: u64, height: u64) {\n        self.width = width;\n        self.height = height;\n    }\n\n    fn move_position(&mut self, point: Point) {\n        self.position = point;\n    }\n\n    fn echo(&mut self, s: String) {\n        self.message = s;\n    }\n\n    fn change_color(&mut self, red: u8, green: u8, blue: u8) {\n        self.color = (red, green, blue);\n    }\n\n    fn quit(&mut self) {\n        self.quit = true;\n    }\n\n    fn process(&mut self, message: Message) {\n        // TODO: Create a match expression to process the different message\n        // variants using the methods defined above.\n    }\n}\n\nfn main() {\n    // You can optionally experiment here.\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    #[test]\n    fn test_match_message_call() {\n        let mut state = State {\n            width: 0,\n            height: 0,\n            position: Point { x: 0, y: 0 },\n            message: String::from(\"hello world\"),\n            color: (0, 0, 0),\n            quit: false,\n        };\n\n        state.process(Message::Resize {\n            width: 10,\n            height: 30,\n        });\n        state.process(Message::Move(Point { x: 10, y: 15 }));\n        state.process(Message::Echo(String::from(\"Hello world!\")));\n        state.process(Message::ChangeColor(255, 0, 255));\n        state.process(Message::Quit);\n\n        assert_eq!(state.width, 10);\n        assert_eq!(state.height, 30);\n        assert_eq!(state.position.x, 10);\n        assert_eq!(state.position.y, 15);\n        assert_eq!(state.message, \"Hello world!\");\n        assert_eq!(state.color, (255, 0, 255));\n        assert!(state.quit);\n    }\n}\n"
  },
  {
    "path": "exercises/09_strings/README.md",
    "content": "# Strings\n\nRust has two string types: a string slice (`&str`) and an owned string (`String`).\nWe're not going to dictate when you should use which one, but we'll show you how\nto identify and create them, as well as use them.\n\n## Further information\n\n- [Strings](https://doc.rust-lang.org/book/ch08-02-strings.html)\n"
  },
  {
    "path": "exercises/09_strings/strings1.rs",
    "content": "// TODO: Fix the compiler error without changing the function signature.\nfn current_favorite_color() -> String {\n    \"blue\"\n}\n\nfn main() {\n    let answer = current_favorite_color();\n    println!(\"My current favorite color is {answer}\");\n}\n"
  },
  {
    "path": "exercises/09_strings/strings2.rs",
    "content": "// TODO: Fix the compiler error in the `main` function without changing this function.\nfn is_a_color_word(attempt: &str) -> bool {\n    attempt == \"green\" || attempt == \"blue\" || attempt == \"red\"\n}\n\nfn main() {\n    let word = String::from(\"green\"); // Don't change this line.\n\n    if is_a_color_word(word) {\n        println!(\"That is a color word I know!\");\n    } else {\n        println!(\"That is not a color word I know.\");\n    }\n}\n"
  },
  {
    "path": "exercises/09_strings/strings3.rs",
    "content": "fn trim_me(input: &str) -> &str {\n    // TODO: Remove whitespace from both ends of a string.\n}\n\nfn compose_me(input: &str) -> String {\n    // TODO: Add \" world!\" to the string! There are multiple ways to do this.\n}\n\nfn replace_me(input: &str) -> String {\n    // TODO: Replace \"cars\" in the string with \"balloons\".\n}\n\nfn main() {\n    // You can optionally experiment here.\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    #[test]\n    fn trim_a_string() {\n        assert_eq!(trim_me(\"Hello!     \"), \"Hello!\");\n        assert_eq!(trim_me(\"  What's up!\"), \"What's up!\");\n        assert_eq!(trim_me(\"   Hola!  \"), \"Hola!\");\n        assert_eq!(trim_me(\"Hi!\"), \"Hi!\");\n    }\n\n    #[test]\n    fn compose_a_string() {\n        assert_eq!(compose_me(\"Hello\"), \"Hello world!\");\n        assert_eq!(compose_me(\"Goodbye\"), \"Goodbye world!\");\n    }\n\n    #[test]\n    fn replace_a_string() {\n        assert_eq!(\n            replace_me(\"I think cars are cool\"),\n            \"I think balloons are cool\",\n        );\n        assert_eq!(\n            replace_me(\"I love to look at cars\"),\n            \"I love to look at balloons\",\n        );\n    }\n}\n"
  },
  {
    "path": "exercises/09_strings/strings4.rs",
    "content": "// Calls of this function should be replaced with calls of `string_slice` or `string`.\nfn placeholder() {}\n\nfn string_slice(arg: &str) {\n    println!(\"{arg}\");\n}\n\nfn string(arg: String) {\n    println!(\"{arg}\");\n}\n\n// TODO: Here are a bunch of values - some are `String`, some are `&str`.\n// Your task is to replace `placeholder(…)` with either `string_slice(…)`\n// or `string(…)` depending on what you think each value is.\nfn main() {\n    placeholder(\"blue\");\n\n    placeholder(\"red\".to_string());\n\n    placeholder(String::from(\"hi\"));\n\n    placeholder(\"rust is fun!\".to_owned());\n\n    placeholder(\"nice weather\".into());\n\n    placeholder(format!(\"Interpolation {}\", \"Station\"));\n\n    // WARNING: This is byte indexing, not character indexing.\n    // Character indexing can be done using `s.chars().nth(INDEX)`.\n    placeholder(&String::from(\"abc\")[0..1]);\n\n    placeholder(\"  hello there \".trim());\n\n    placeholder(\"Happy Monday!\".replace(\"Mon\", \"Tues\"));\n\n    placeholder(\"mY sHiFt KeY iS sTiCkY\".to_lowercase());\n}\n"
  },
  {
    "path": "exercises/10_modules/README.md",
    "content": "# Modules\n\nIn this section we'll give you an introduction to Rust's module system.\n\n## Further information\n\n- [The Module System](https://doc.rust-lang.org/book/ch07-00-managing-growing-projects-with-packages-crates-and-modules.html)\n"
  },
  {
    "path": "exercises/10_modules/modules1.rs",
    "content": "// TODO: Fix the compiler error about calling a private function.\nmod sausage_factory {\n    // Don't let anybody outside of this module see this!\n    fn get_secret_recipe() -> String {\n        String::from(\"Ginger\")\n    }\n\n    fn make_sausage() {\n        get_secret_recipe();\n        println!(\"sausage!\");\n    }\n}\n\nfn main() {\n    sausage_factory::make_sausage();\n}\n"
  },
  {
    "path": "exercises/10_modules/modules2.rs",
    "content": "// You can bring module paths into scopes and provide new names for them with\n// the `use` and `as` keywords.\n\nmod delicious_snacks {\n    // TODO: Add the following two `use` statements after fixing them.\n    // use self::fruits::PEAR as ???;\n    // use self::veggies::CUCUMBER as ???;\n\n    mod fruits {\n        pub const PEAR: &str = \"Pear\";\n        pub const APPLE: &str = \"Apple\";\n    }\n\n    mod veggies {\n        pub const CUCUMBER: &str = \"Cucumber\";\n        pub const CARROT: &str = \"Carrot\";\n    }\n}\n\nfn main() {\n    println!(\n        \"favorite snacks: {} and {}\",\n        delicious_snacks::fruit,\n        delicious_snacks::veggie,\n    );\n}\n"
  },
  {
    "path": "exercises/10_modules/modules3.rs",
    "content": "// You can use the `use` keyword to bring module paths from modules from\n// anywhere and especially from the standard library into your scope.\n\n// TODO: Bring `SystemTime` and `UNIX_EPOCH` from the `std::time` module into\n// your scope. Bonus style points if you can do it with one line!\n// use ???;\n\nfn main() {\n    match SystemTime::now().duration_since(UNIX_EPOCH) {\n        Ok(n) => println!(\"1970-01-01 00:00:00 UTC was {} seconds ago!\", n.as_secs()),\n        Err(_) => panic!(\"SystemTime before UNIX EPOCH!\"),\n    }\n}\n"
  },
  {
    "path": "exercises/11_hashmaps/README.md",
    "content": "# Hashmaps\n\nA *hash map* allows you to associate a value with a particular key.\nYou may also know this by the names [*unordered map* in C++](https://en.cppreference.com/w/cpp/container/unordered_map),\n[*dictionary* in Python](https://docs.python.org/3/tutorial/datastructures.html#dictionaries) or an *associative array* in other languages.\n\nThis is the other data structure that we've been talking about before, when\ntalking about Vecs.\n\n## Further information\n\n- [Storing Keys with Associated Values in Hash Maps](https://doc.rust-lang.org/book/ch08-03-hash-maps.html)\n"
  },
  {
    "path": "exercises/11_hashmaps/hashmaps1.rs",
    "content": "// A basket of fruits in the form of a hash map needs to be defined. The key\n// represents the name of the fruit and the value represents how many of that\n// particular fruit is in the basket. You have to put at least 3 different\n// types of fruits (e.g. apple, banana, mango) in the basket and the total count\n// of all the fruits should be at least 5.\n\nuse std::collections::HashMap;\n\nfn fruit_basket() -> HashMap<String, u32> {\n    // TODO: Declare the hash map.\n    // let mut basket =\n\n    // Two bananas are already given for you :)\n    basket.insert(String::from(\"banana\"), 2);\n\n    // TODO: Put more fruits in your basket.\n\n    basket\n}\n\nfn main() {\n    // You can optionally experiment here.\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    #[test]\n    fn at_least_three_types_of_fruits() {\n        let basket = fruit_basket();\n        assert!(basket.len() >= 3);\n    }\n\n    #[test]\n    fn at_least_five_fruits() {\n        let basket = fruit_basket();\n        assert!(basket.values().sum::<u32>() >= 5);\n    }\n}\n"
  },
  {
    "path": "exercises/11_hashmaps/hashmaps2.rs",
    "content": "// We're collecting different fruits to bake a delicious fruit cake. For this,\n// we have a basket, which we'll represent in the form of a hash map. The key\n// represents the name of each fruit we collect and the value represents how\n// many of that particular fruit we have collected. Three types of fruits -\n// Apple (4), Mango (2) and Lychee (5) are already in the basket hash map. You\n// must add fruit to the basket so that there is at least one of each kind and\n// more than 11 in total - we have a lot of mouths to feed. You are not allowed\n// to insert any more of the fruits that are already in the basket (Apple,\n// Mango, and Lychee).\n\nuse std::collections::HashMap;\n\n#[derive(Hash, PartialEq, Eq, Debug)]\nenum Fruit {\n    Apple,\n    Banana,\n    Mango,\n    Lychee,\n    Pineapple,\n}\n\nfn fruit_basket(basket: &mut HashMap<Fruit, u32>) {\n    let fruit_kinds = [\n        Fruit::Apple,\n        Fruit::Banana,\n        Fruit::Mango,\n        Fruit::Lychee,\n        Fruit::Pineapple,\n    ];\n\n    for fruit in fruit_kinds {\n        // TODO: Insert new fruits if they are not already present in the\n        // basket. Note that you are not allowed to put any type of fruit that's\n        // already present!\n    }\n}\n\nfn main() {\n    // You can optionally experiment here.\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    // Don't modify this function!\n    fn get_fruit_basket() -> HashMap<Fruit, u32> {\n        let content = [(Fruit::Apple, 4), (Fruit::Mango, 2), (Fruit::Lychee, 5)];\n        HashMap::from_iter(content)\n    }\n\n    #[test]\n    fn test_given_fruits_are_not_modified() {\n        let mut basket = get_fruit_basket();\n        fruit_basket(&mut basket);\n        assert_eq!(*basket.get(&Fruit::Apple).unwrap(), 4);\n        assert_eq!(*basket.get(&Fruit::Mango).unwrap(), 2);\n        assert_eq!(*basket.get(&Fruit::Lychee).unwrap(), 5);\n    }\n\n    #[test]\n    fn at_least_five_types_of_fruits() {\n        let mut basket = get_fruit_basket();\n        fruit_basket(&mut basket);\n        let count_fruit_kinds = basket.len();\n        assert!(count_fruit_kinds >= 5);\n    }\n\n    #[test]\n    fn greater_than_eleven_fruits() {\n        let mut basket = get_fruit_basket();\n        fruit_basket(&mut basket);\n        let count = basket.values().sum::<u32>();\n        assert!(count > 11);\n    }\n\n    #[test]\n    fn all_fruit_types_in_basket() {\n        let fruit_kinds = [\n            Fruit::Apple,\n            Fruit::Banana,\n            Fruit::Mango,\n            Fruit::Lychee,\n            Fruit::Pineapple,\n        ];\n\n        let mut basket = get_fruit_basket();\n        fruit_basket(&mut basket);\n\n        for fruit_kind in fruit_kinds {\n            let Some(amount) = basket.get(&fruit_kind) else {\n                panic!(\"Fruit kind {fruit_kind:?} was not found in basket\");\n            };\n            assert!(*amount > 0);\n        }\n    }\n}\n"
  },
  {
    "path": "exercises/11_hashmaps/hashmaps3.rs",
    "content": "// A list of scores (one per line) of a soccer match is given. Each line is of\n// the form \"<team_1_name>,<team_2_name>,<team_1_goals>,<team_2_goals>\"\n// Example: \"England,France,4,2\" (England scored 4 goals, France 2).\n//\n// You have to build a scores table containing the name of the team, the total\n// number of goals the team scored, and the total number of goals the team\n// conceded.\n\nuse std::collections::HashMap;\n\n// A structure to store the goal details of a team.\n#[derive(Default)]\nstruct TeamScores {\n    goals_scored: u8,\n    goals_conceded: u8,\n}\n\nfn build_scores_table(results: &str) -> HashMap<&str, TeamScores> {\n    // The name of the team is the key and its associated struct is the value.\n    let mut scores = HashMap::<&str, TeamScores>::new();\n\n    for line in results.lines() {\n        let mut split_iterator = line.split(',');\n        // NOTE: We use `unwrap` because we didn't deal with error handling yet.\n        let team_1_name = split_iterator.next().unwrap();\n        let team_2_name = split_iterator.next().unwrap();\n        let team_1_score: u8 = split_iterator.next().unwrap().parse().unwrap();\n        let team_2_score: u8 = split_iterator.next().unwrap().parse().unwrap();\n\n        // TODO: Populate the scores table with the extracted details.\n        // Keep in mind that goals scored by team 1 will be the number of goals\n        // conceded by team 2. Similarly, goals scored by team 2 will be the\n        // number of goals conceded by team 1.\n    }\n\n    scores\n}\n\nfn main() {\n    // You can optionally experiment here.\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    const RESULTS: &str = \"England,France,4,2\nFrance,Italy,3,1\nPoland,Spain,2,0\nGermany,England,2,1\nEngland,Spain,1,0\";\n\n    #[test]\n    fn build_scores() {\n        let scores = build_scores_table(RESULTS);\n\n        assert!([\"England\", \"France\", \"Germany\", \"Italy\", \"Poland\", \"Spain\"]\n            .into_iter()\n            .all(|team_name| scores.contains_key(team_name)));\n    }\n\n    #[test]\n    fn validate_team_score_1() {\n        let scores = build_scores_table(RESULTS);\n        let team = scores.get(\"England\").unwrap();\n        assert_eq!(team.goals_scored, 6);\n        assert_eq!(team.goals_conceded, 4);\n    }\n\n    #[test]\n    fn validate_team_score_2() {\n        let scores = build_scores_table(RESULTS);\n        let team = scores.get(\"Spain\").unwrap();\n        assert_eq!(team.goals_scored, 0);\n        assert_eq!(team.goals_conceded, 3);\n    }\n}\n"
  },
  {
    "path": "exercises/12_options/README.md",
    "content": "# Options\n\nType Option represents an optional value: every Option is either Some and contains a value, or None, and does not.\nOption types are very common in Rust code, as they have a number of uses:\n\n- Initial values\n- Return values for functions that are not defined over their entire input range (partial functions)\n- Return value for otherwise reporting simple errors, where None is returned on error\n- Optional struct fields\n- Struct fields that can be loaned or \"taken\"\n- Optional function arguments\n- Nullable pointers\n- Swapping things out of difficult situations\n\n## Further Information\n\n- [Option Enum Format](https://doc.rust-lang.org/book/ch10-01-syntax.html#in-enum-definitions)\n- [Option Module Documentation](https://doc.rust-lang.org/std/option/)\n- [Option Enum Documentation](https://doc.rust-lang.org/std/option/enum.Option.html)\n- [if let](https://doc.rust-lang.org/rust-by-example/flow_control/if_let.html)\n- [while let](https://doc.rust-lang.org/rust-by-example/flow_control/while_let.html)\n"
  },
  {
    "path": "exercises/12_options/options1.rs",
    "content": "// This function returns how much ice cream there is left in the fridge.\n// If it's before 22:00 (24-hour system), then 5 scoops are left. At 22:00,\n// someone eats it all, so no ice cream is left (value 0). Return `None` if\n// `hour_of_day` is higher than 23.\nfn maybe_ice_cream(hour_of_day: u16) -> Option<u16> {\n    // TODO: Complete the function body.\n}\n\nfn main() {\n    // You can optionally experiment here.\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    #[test]\n    fn raw_value() {\n        // TODO: Fix this test. How do you get the value contained in the\n        // Option?\n        let ice_creams = maybe_ice_cream(12);\n\n        assert_eq!(ice_creams, 5); // Don't change this line.\n    }\n\n    #[test]\n    fn check_ice_cream() {\n        assert_eq!(maybe_ice_cream(0), Some(5));\n        assert_eq!(maybe_ice_cream(9), Some(5));\n        assert_eq!(maybe_ice_cream(18), Some(5));\n        assert_eq!(maybe_ice_cream(22), Some(0));\n        assert_eq!(maybe_ice_cream(23), Some(0));\n        assert_eq!(maybe_ice_cream(24), None);\n        assert_eq!(maybe_ice_cream(25), None);\n    }\n}\n"
  },
  {
    "path": "exercises/12_options/options2.rs",
    "content": "fn main() {\n    // You can optionally experiment here.\n}\n\n#[cfg(test)]\nmod tests {\n    #[test]\n    fn simple_option() {\n        let target = \"rustlings\";\n        let optional_target = Some(target);\n\n        // TODO: Make this an if-let statement whose value is `Some`.\n        word = optional_target {\n            assert_eq!(word, target);\n        }\n    }\n\n    #[test]\n    fn layered_option() {\n        let range = 10;\n        let mut optional_integers: Vec<Option<i8>> = vec![None];\n\n        for i in 1..=range {\n            optional_integers.push(Some(i));\n        }\n\n        let mut cursor = range;\n\n        // TODO: Make this a while-let statement. Remember that `Vec::pop()`\n        // adds another layer of `Option`. You can do nested pattern matching\n        // in if-let and while-let statements.\n        integer = optional_integers.pop() {\n            assert_eq!(integer, cursor);\n            cursor -= 1;\n        }\n\n        assert_eq!(cursor, 0);\n    }\n}\n"
  },
  {
    "path": "exercises/12_options/options3.rs",
    "content": "#[derive(Debug)]\nstruct Point {\n    x: i32,\n    y: i32,\n}\n\nfn main() {\n    let optional_point = Some(Point { x: 100, y: 200 });\n\n    // TODO: Fix the compiler error by adding something to this match statement.\n    match optional_point {\n        Some(p) => println!(\"Coordinates are {},{}\", p.x, p.y),\n        _ => panic!(\"No match!\"),\n    }\n\n    println!(\"{optional_point:?}\"); // Don't change this line.\n}\n"
  },
  {
    "path": "exercises/13_error_handling/README.md",
    "content": "# Error handling\n\nMost errors aren't serious enough to require the program to stop entirely.\nSometimes, when a function fails, it's for a reason that you can easily interpret and respond to.\nFor example, if you try to open a file and that operation fails because the file doesn't exist, you might want to create the file instead of terminating the process.\n\n## Further information\n\n- [Error Handling](https://doc.rust-lang.org/book/ch09-02-recoverable-errors-with-result.html)\n- [Generics](https://doc.rust-lang.org/book/ch10-01-syntax.html)\n- [Result](https://doc.rust-lang.org/rust-by-example/error/result.html)\n- [Boxing errors](https://doc.rust-lang.org/rust-by-example/error/multiple_error_types/boxing_errors.html)\n"
  },
  {
    "path": "exercises/13_error_handling/errors1.rs",
    "content": "// TODO: This function refuses to generate text to be printed on a nametag if\n// you pass it an empty string. It'd be nicer if it explained what the problem\n// was instead of just returning `None`. Thankfully, Rust has a similar\n// construct to `Option` that can be used to express error conditions. Change\n// the function signature and body to return `Result<String, String>` instead\n// of `Option<String>`.\nfn generate_nametag_text(name: String) -> Option<String> {\n    if name.is_empty() {\n        // Empty names aren't allowed\n        None\n    } else {\n        Some(format!(\"Hi! My name is {name}\"))\n    }\n}\n\nfn main() {\n    // You can optionally experiment here.\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    #[test]\n    fn generates_nametag_text_for_a_nonempty_name() {\n        assert_eq!(\n            generate_nametag_text(\"Beyoncé\".to_string()).as_deref(),\n            Ok(\"Hi! My name is Beyoncé\"),\n        );\n    }\n\n    #[test]\n    fn explains_why_generating_nametag_text_fails() {\n        assert_eq!(\n            generate_nametag_text(String::new())\n                .as_ref()\n                .map_err(|e| e.as_str()),\n            Err(\"Empty names aren't allowed\"),\n        );\n    }\n}\n"
  },
  {
    "path": "exercises/13_error_handling/errors2.rs",
    "content": "// Say we're writing a game where you can buy items with tokens. All items cost\n// 5 tokens, and whenever you purchase items there is a processing fee of 1\n// token. A player of the game will type in how many items they want to buy, and\n// the `total_cost` function will calculate the total cost of the items. Since\n// the player typed in the quantity, we get it as a string. They might have\n// typed anything, not just numbers!\n//\n// Right now, this function isn't handling the error case at all. What we want\n// to do is: If we call the `total_cost` function on a string that is not a\n// number, that function will return a `ParseIntError`. In that case, we want to\n// immediately return that error from our function and not try to multiply and\n// add.\n//\n// There are at least two ways to implement this that are both correct. But one\n// is a lot shorter!\n\nuse std::num::ParseIntError;\n\nfn total_cost(item_quantity: &str) -> Result<i32, ParseIntError> {\n    let processing_fee = 1;\n    let cost_per_item = 5;\n\n    // TODO: Handle the error case as described above.\n    let qty = item_quantity.parse::<i32>();\n\n    Ok(qty * cost_per_item + processing_fee)\n}\n\nfn main() {\n    // You can optionally experiment here.\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n    use std::num::IntErrorKind;\n\n    #[test]\n    fn item_quantity_is_a_valid_number() {\n        assert_eq!(total_cost(\"34\"), Ok(171));\n    }\n\n    #[test]\n    fn item_quantity_is_an_invalid_number() {\n        assert_eq!(\n            total_cost(\"beep boop\").unwrap_err().kind(),\n            &IntErrorKind::InvalidDigit,\n        );\n    }\n}\n"
  },
  {
    "path": "exercises/13_error_handling/errors3.rs",
    "content": "// This is a program that is trying to use a completed version of the\n// `total_cost` function from the previous exercise. It's not working though!\n// Why not? What should we do to fix it?\n\nuse std::num::ParseIntError;\n\n// Don't change this function.\nfn total_cost(item_quantity: &str) -> Result<i32, ParseIntError> {\n    let processing_fee = 1;\n    let cost_per_item = 5;\n    let qty = item_quantity.parse::<i32>()?;\n\n    Ok(qty * cost_per_item + processing_fee)\n}\n\n// TODO: Fix the compiler error by changing the signature and body of the\n// `main` function.\nfn main() {\n    let mut tokens = 100;\n    let pretend_user_input = \"8\";\n\n    // Don't change this line.\n    let cost = total_cost(pretend_user_input)?;\n\n    if cost > tokens {\n        println!(\"You can't afford that many!\");\n    } else {\n        tokens -= cost;\n        println!(\"You now have {tokens} tokens.\");\n    }\n}\n"
  },
  {
    "path": "exercises/13_error_handling/errors4.rs",
    "content": "#[derive(PartialEq, Debug)]\nenum CreationError {\n    Negative,\n    Zero,\n}\n\n#[derive(PartialEq, Debug)]\nstruct PositiveNonzeroInteger(u64);\n\nimpl PositiveNonzeroInteger {\n    fn new(value: i64) -> Result<Self, CreationError> {\n        // TODO: This function shouldn't always return an `Ok`.\n        // Read the tests below to clarify what should be returned.\n        Ok(Self(value as u64))\n    }\n}\n\nfn main() {\n    // You can optionally experiment here.\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    #[test]\n    fn test_creation() {\n        assert_eq!(\n            PositiveNonzeroInteger::new(10),\n            Ok(PositiveNonzeroInteger(10)),\n        );\n        assert_eq!(\n            PositiveNonzeroInteger::new(-10),\n            Err(CreationError::Negative),\n        );\n        assert_eq!(PositiveNonzeroInteger::new(0), Err(CreationError::Zero));\n    }\n}\n"
  },
  {
    "path": "exercises/13_error_handling/errors5.rs",
    "content": "// This exercise is an altered version of the `errors4` exercise. It uses some\n// concepts that we won't get to until later in the course, like `Box` and the\n// `From` trait. It's not important to understand them in detail right now, but\n// you can read ahead if you like. For now, think of the `Box<dyn ???>` type as\n// an \"I want anything that does ???\" type.\n//\n// In short, this particular use case for boxes is for when you want to own a\n// value and you care only that it is a type which implements a particular\n// trait. To do so, the `Box` is declared as of type `Box<dyn Trait>` where\n// `Trait` is the trait the compiler looks for on any value used in that\n// context. For this exercise, that context is the potential errors which\n// can be returned in a `Result`.\n\nuse std::error::Error;\nuse std::fmt;\n\n#[derive(PartialEq, Debug)]\nenum CreationError {\n    Negative,\n    Zero,\n}\n\n// This is required so that `CreationError` can implement `Error`.\nimpl fmt::Display for CreationError {\n    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {\n        let description = match *self {\n            CreationError::Negative => \"number is negative\",\n            CreationError::Zero => \"number is zero\",\n        };\n        f.write_str(description)\n    }\n}\n\nimpl Error for CreationError {}\n\n#[derive(PartialEq, Debug)]\nstruct PositiveNonzeroInteger(u64);\n\nimpl PositiveNonzeroInteger {\n    fn new(value: i64) -> Result<PositiveNonzeroInteger, CreationError> {\n        match value {\n            x if x < 0 => Err(CreationError::Negative),\n            0 => Err(CreationError::Zero),\n            x => Ok(PositiveNonzeroInteger(x as u64)),\n        }\n    }\n}\n\n// TODO: Add the correct return type `Result<(), Box<dyn ???>>`. What can we\n// use to describe both errors? Is there a trait which both errors implement?\nfn main() {\n    let pretend_user_input = \"42\";\n    let x: i64 = pretend_user_input.parse()?;\n    println!(\"output={:?}\", PositiveNonzeroInteger::new(x)?);\n    Ok(())\n}\n"
  },
  {
    "path": "exercises/13_error_handling/errors6.rs",
    "content": "// Using catch-all error types like `Box<dyn Error>` isn't recommended for\n// library code where callers might want to make decisions based on the error\n// content instead of printing it out or propagating it further. Here, we define\n// a custom error type to make it possible for callers to decide what to do next\n// when our function returns an error.\n\nuse std::num::ParseIntError;\n\n#[derive(PartialEq, Debug)]\nenum CreationError {\n    Negative,\n    Zero,\n}\n\n// A custom error type that we will be using in `PositiveNonzeroInteger::parse`.\n#[derive(PartialEq, Debug)]\nenum ParsePosNonzeroError {\n    Creation(CreationError),\n    ParseInt(ParseIntError),\n}\n\nimpl ParsePosNonzeroError {\n    fn from_creation(err: CreationError) -> Self {\n        Self::Creation(err)\n    }\n\n    // TODO: Add another error conversion function here.\n    // fn from_parse_int(???) -> Self { ??? }\n}\n\n#[derive(PartialEq, Debug)]\nstruct PositiveNonzeroInteger(u64);\n\nimpl PositiveNonzeroInteger {\n    fn new(value: i64) -> Result<Self, CreationError> {\n        match value {\n            x if x < 0 => Err(CreationError::Negative),\n            0 => Err(CreationError::Zero),\n            x => Ok(Self(x as u64)),\n        }\n    }\n\n    fn parse(s: &str) -> Result<Self, ParsePosNonzeroError> {\n        // TODO: change this to return an appropriate error instead of panicking\n        // when `parse()` returns an error.\n        let x: i64 = s.parse().unwrap();\n        Self::new(x).map_err(ParsePosNonzeroError::from_creation)\n    }\n}\n\nfn main() {\n    // You can optionally experiment here.\n}\n\n#[cfg(test)]\nmod test {\n    use super::*;\n\n    #[test]\n    fn test_parse_error() {\n        assert!(matches!(\n            PositiveNonzeroInteger::parse(\"not a number\"),\n            Err(ParsePosNonzeroError::ParseInt(_)),\n        ));\n    }\n\n    #[test]\n    fn test_negative() {\n        assert_eq!(\n            PositiveNonzeroInteger::parse(\"-555\"),\n            Err(ParsePosNonzeroError::Creation(CreationError::Negative)),\n        );\n    }\n\n    #[test]\n    fn test_zero() {\n        assert_eq!(\n            PositiveNonzeroInteger::parse(\"0\"),\n            Err(ParsePosNonzeroError::Creation(CreationError::Zero)),\n        );\n    }\n\n    #[test]\n    fn test_positive() {\n        let x = PositiveNonzeroInteger::new(42).unwrap();\n        assert_eq!(x.0, 42);\n        assert_eq!(PositiveNonzeroInteger::parse(\"42\"), Ok(x));\n    }\n}\n"
  },
  {
    "path": "exercises/14_generics/README.md",
    "content": "# Generics\n\nGenerics is the topic of generalizing types and functionalities to broader cases.\nThis is extremely useful for reducing code duplication in many ways, but can call for some rather involved syntax.\nNamely, being generic requires taking great care to specify over which types a generic type is actually considered valid.\nThe simplest and most common use of generics is for type parameters.\n\n## Further information\n\n- [Generic Data Types](https://doc.rust-lang.org/book/ch10-01-syntax.html)\n- [Bounds](https://doc.rust-lang.org/rust-by-example/generics/bounds.html)\n"
  },
  {
    "path": "exercises/14_generics/generics1.rs",
    "content": "// `Vec<T>` is generic over the type `T`. In most cases, the compiler is able to\n// infer `T`, for example after pushing a value with a concrete type to the vector.\n// But in this exercise, the compiler needs some help through a type annotation.\n\nfn main() {\n    // TODO: Fix the compiler error by annotating the type of the vector\n    // `Vec<T>`. Choose `T` as some integer type that can be created from\n    // `u8` and `i8`.\n    let mut numbers = Vec::new();\n\n    // Don't change the lines below.\n    let n1: u8 = 42;\n    numbers.push(n1.into());\n    let n2: i8 = -1;\n    numbers.push(n2.into());\n\n    println!(\"{numbers:?}\");\n}\n"
  },
  {
    "path": "exercises/14_generics/generics2.rs",
    "content": "// This powerful wrapper provides the ability to store a positive integer value.\n// TODO: Rewrite it using a generic so that it supports wrapping ANY type.\nstruct Wrapper {\n    value: u32,\n}\n\n// TODO: Adapt the struct's implementation to be generic over the wrapped value.\nimpl Wrapper {\n    fn new(value: u32) -> Self {\n        Wrapper { value }\n    }\n}\n\nfn main() {\n    // You can optionally experiment here.\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    #[test]\n    fn store_u32_in_wrapper() {\n        assert_eq!(Wrapper::new(42).value, 42);\n    }\n\n    #[test]\n    fn store_str_in_wrapper() {\n        assert_eq!(Wrapper::new(\"Foo\").value, \"Foo\");\n    }\n}\n"
  },
  {
    "path": "exercises/15_traits/README.md",
    "content": "# Traits\n\nA trait is a collection of methods.\n\nData types can implement traits. To do so, the methods making up the trait are defined for the data type. For example, the `String` data type implements the `From<&str>` trait. This allows a user to write `String::from(\"hello\")`.\n\nIn this way, traits are somewhat similar to Java interfaces and C++ abstract classes.\n\nSome additional common Rust traits include:\n\n- `Clone` (the `clone` method)\n- `Display` (which allows formatted display via `{}`)\n- `Debug` (which allows formatted display via `{:?}`)\n\nBecause traits indicate shared behavior between data types, they are useful when writing generics.\n\n## Further information\n\n- [Traits](https://doc.rust-lang.org/book/ch10-02-traits.html)\n"
  },
  {
    "path": "exercises/15_traits/traits1.rs",
    "content": "// The trait `AppendBar` has only one function which appends \"Bar\" to any object\n// implementing this trait.\ntrait AppendBar {\n    fn append_bar(self) -> Self;\n}\n\nimpl AppendBar for String {\n    // TODO: Implement `AppendBar` for the type `String`.\n}\n\nfn main() {\n    let s = String::from(\"Foo\");\n    let s = s.append_bar();\n    println!(\"s: {s}\");\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    #[test]\n    fn is_foo_bar() {\n        assert_eq!(String::from(\"Foo\").append_bar(), \"FooBar\");\n    }\n\n    #[test]\n    fn is_bar_bar() {\n        assert_eq!(String::from(\"\").append_bar().append_bar(), \"BarBar\");\n    }\n}\n"
  },
  {
    "path": "exercises/15_traits/traits2.rs",
    "content": "trait AppendBar {\n    fn append_bar(self) -> Self;\n}\n\n// TODO: Implement the trait `AppendBar` for a vector of strings.\n// `append_bar` should push the string \"Bar\" into the vector.\n\nfn main() {\n    // You can optionally experiment here.\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    #[test]\n    fn is_vec_pop_eq_bar() {\n        let mut foo = vec![String::from(\"Foo\")].append_bar();\n        assert_eq!(foo.pop().unwrap(), \"Bar\");\n        assert_eq!(foo.pop().unwrap(), \"Foo\");\n    }\n}\n"
  },
  {
    "path": "exercises/15_traits/traits3.rs",
    "content": "trait Licensed {\n    // TODO: Add a default implementation for `licensing_info` so that\n    // implementors like the two structs below can share that default behavior\n    // without repeating the function.\n    // The default license information should be the string \"Default license\".\n    fn licensing_info(&self) -> String;\n}\n\nstruct SomeSoftware {\n    version_number: i32,\n}\n\nstruct OtherSoftware {\n    version_number: String,\n}\n\nimpl Licensed for SomeSoftware {} // Don't edit this line.\nimpl Licensed for OtherSoftware {} // Don't edit this line.\n\nfn main() {\n    // You can optionally experiment here.\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    #[test]\n    fn is_licensing_info_the_same() {\n        let licensing_info = \"Default license\";\n        let some_software = SomeSoftware { version_number: 1 };\n        let other_software = OtherSoftware {\n            version_number: \"v2.0.0\".to_string(),\n        };\n        assert_eq!(some_software.licensing_info(), licensing_info);\n        assert_eq!(other_software.licensing_info(), licensing_info);\n    }\n}\n"
  },
  {
    "path": "exercises/15_traits/traits4.rs",
    "content": "trait Licensed {\n    fn licensing_info(&self) -> String {\n        \"Default license\".to_string()\n    }\n}\n\nstruct SomeSoftware;\nstruct OtherSoftware;\n\nimpl Licensed for SomeSoftware {}\nimpl Licensed for OtherSoftware {}\n\n// TODO: Fix the compiler error by only changing the signature of this function.\nfn compare_license_types(software1: ???, software2: ???) -> bool {\n    software1.licensing_info() == software2.licensing_info()\n}\n\nfn main() {\n    // You can optionally experiment here.\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    #[test]\n    fn compare_license_information() {\n        assert!(compare_license_types(SomeSoftware, OtherSoftware));\n    }\n\n    #[test]\n    fn compare_license_information_backwards() {\n        assert!(compare_license_types(OtherSoftware, SomeSoftware));\n    }\n}\n"
  },
  {
    "path": "exercises/15_traits/traits5.rs",
    "content": "trait SomeTrait {\n    fn some_function(&self) -> bool {\n        true\n    }\n}\n\ntrait OtherTrait {\n    fn other_function(&self) -> bool {\n        true\n    }\n}\n\nstruct SomeStruct;\nimpl SomeTrait for SomeStruct {}\nimpl OtherTrait for SomeStruct {}\n\nstruct OtherStruct;\nimpl SomeTrait for OtherStruct {}\nimpl OtherTrait for OtherStruct {}\n\n// TODO: Fix the compiler error by only changing the signature of this function.\nfn some_func(item: ???) -> bool {\n    item.some_function() && item.other_function()\n}\n\nfn main() {\n    // You can optionally experiment here.\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    #[test]\n    fn test_some_func() {\n        assert!(some_func(SomeStruct));\n        assert!(some_func(OtherStruct));\n    }\n}\n"
  },
  {
    "path": "exercises/16_lifetimes/README.md",
    "content": "# Lifetimes\n\nLifetimes tell the compiler how to check whether references live long\nenough to be valid in any given situation. For example lifetimes say\n\"make sure parameter 'a' lives as long as parameter 'b' so that the return\nvalue is valid\".\n\nThey are only necessary on borrows, i.e. references,\nsince copied parameters or moves are owned in their scope and cannot\nbe referenced outside. Lifetimes mean that calling code of e.g. functions\ncan be checked to make sure their arguments are valid. Lifetimes are\nrestrictive of their callers.\n\nIf you'd like to learn more about lifetime annotations, the\n[lifetimekata](https://tfpk.github.io/lifetimekata/) project\nhas a similar style of exercises to Rustlings, but is all about\nlearning to write lifetime annotations.\n\n## Further information\n\n- [Lifetimes (in Rust By Example)](https://doc.rust-lang.org/stable/rust-by-example/scope/lifetime.html)\n- [Validating References with Lifetimes](https://doc.rust-lang.org/book/ch10-03-lifetime-syntax.html)\n"
  },
  {
    "path": "exercises/16_lifetimes/lifetimes1.rs",
    "content": "// The Rust compiler needs to know how to check whether supplied references are\n// valid, so that it can let the programmer know if a reference is at risk of\n// going out of scope before it is used. Remember, references are borrows and do\n// not own their own data. What if their owner goes out of scope?\n\n// TODO: Fix the compiler error by updating the function signature.\nfn longest(x: &str, y: &str) -> &str {\n    if x.len() > y.len() {\n        x\n    } else {\n        y\n    }\n}\n\nfn main() {\n    // You can optionally experiment here.\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    #[test]\n    fn test_longest() {\n        assert_eq!(longest(\"abcd\", \"123\"), \"abcd\");\n        assert_eq!(longest(\"abc\", \"1234\"), \"1234\");\n    }\n}\n"
  },
  {
    "path": "exercises/16_lifetimes/lifetimes2.rs",
    "content": "// Don't change this function.\nfn longest<'a>(x: &'a str, y: &'a str) -> &'a str {\n    if x.len() > y.len() {\n        x\n    } else {\n        y\n    }\n}\n\nfn main() {\n    // TODO: Fix the compiler error by moving one line.\n\n    let string1 = String::from(\"long string is long\");\n    let result;\n    {\n        let string2 = String::from(\"xyz\");\n        result = longest(&string1, &string2);\n    }\n    println!(\"The longest string is '{result}'\");\n}\n"
  },
  {
    "path": "exercises/16_lifetimes/lifetimes3.rs",
    "content": "// Lifetimes are also needed when structs hold references.\n\n// TODO: Fix the compiler errors about the struct.\nstruct Book {\n    author: &str,\n    title: &str,\n}\n\nfn main() {\n    let book = Book {\n        author: \"George Orwell\",\n        title: \"1984\",\n    };\n\n    println!(\"{} by {}\", book.title, book.author);\n}\n"
  },
  {
    "path": "exercises/17_tests/README.md",
    "content": "# Tests\n\nGoing out of order from the book to cover tests -- many of the following exercises will ask you to make tests pass!\n\n## Further information\n\n- [Writing Tests](https://doc.rust-lang.org/book/ch11-01-writing-tests.html)\n"
  },
  {
    "path": "exercises/17_tests/tests1.rs",
    "content": "// Tests are important to ensure that your code does what you think it should\n// do.\n\nfn is_even(n: i64) -> bool {\n    n % 2 == 0\n}\n\nfn main() {\n    // You can optionally experiment here.\n}\n\n#[cfg(test)]\nmod tests {\n    // TODO: Import `is_even`. You can use a wildcard to import everything in\n    // the outer module.\n\n    #[test]\n    fn you_can_assert() {\n        // TODO: Test the function `is_even` with some values.\n        assert!();\n        assert!();\n    }\n}\n"
  },
  {
    "path": "exercises/17_tests/tests2.rs",
    "content": "// Calculates the power of 2 using a bit shift.\n// `1 << n` is equivalent to \"2 to the power of n\".\nfn power_of_2(n: u8) -> u64 {\n    1 << n\n}\n\nfn main() {\n    // You can optionally experiment here.\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    #[test]\n    fn you_can_assert_eq() {\n        // TODO: Test the function `power_of_2` with some values.\n        assert_eq!();\n        assert_eq!();\n        assert_eq!();\n        assert_eq!();\n    }\n}\n"
  },
  {
    "path": "exercises/17_tests/tests3.rs",
    "content": "struct Rectangle {\n    width: i32,\n    height: i32,\n}\n\nimpl Rectangle {\n    // Don't change this function.\n    fn new(width: i32, height: i32) -> Self {\n        if width <= 0 || height <= 0 {\n            // Returning a `Result` would be better here. But we want to learn\n            // how to test functions that can panic.\n            panic!(\"Rectangle width and height must be positive\");\n        }\n\n        Rectangle { width, height }\n    }\n}\n\nfn main() {\n    // You can optionally experiment here.\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    #[test]\n    fn correct_width_and_height() {\n        // TODO: This test should check if the rectangle has the size that we\n        // pass to its constructor.\n        let rect = Rectangle::new(10, 20);\n        assert_eq!(todo!(), 10); // Check width\n        assert_eq!(todo!(), 20); // Check height\n    }\n\n    // TODO: This test should check if the program panics when we try to create\n    // a rectangle with negative width.\n    #[test]\n    fn negative_width() {\n        let _rect = Rectangle::new(-10, 10);\n    }\n\n    // TODO: This test should check if the program panics when we try to create\n    // a rectangle with negative height.\n    #[test]\n    fn negative_height() {\n        let _rect = Rectangle::new(10, -10);\n    }\n}\n"
  },
  {
    "path": "exercises/18_iterators/README.md",
    "content": "# Iterators\n\nThis section will teach you about Iterators.\n\n## Further information\n\n- [Iterator](https://doc.rust-lang.org/book/ch13-02-iterators.html)\n- [Iterator documentation](https://doc.rust-lang.org/stable/std/iter/)\n"
  },
  {
    "path": "exercises/18_iterators/iterators1.rs",
    "content": "// When performing operations on elements within a collection, iterators are\n// essential. This module helps you get familiar with the structure of using an\n// iterator and how to go through elements within an iterable collection.\n\nfn main() {\n    // You can optionally experiment here.\n}\n\n#[cfg(test)]\nmod tests {\n    #[test]\n    fn iterators() {\n        let my_fav_fruits = [\"banana\", \"custard apple\", \"avocado\", \"peach\", \"raspberry\"];\n\n        // TODO: Create an iterator over the array.\n        let mut fav_fruits_iterator = todo!();\n\n        assert_eq!(fav_fruits_iterator.next(), Some(&\"banana\"));\n        assert_eq!(fav_fruits_iterator.next(), todo!()); // TODO: Replace `todo!()`\n        assert_eq!(fav_fruits_iterator.next(), Some(&\"avocado\"));\n        assert_eq!(fav_fruits_iterator.next(), todo!()); // TODO: Replace `todo!()`\n        assert_eq!(fav_fruits_iterator.next(), Some(&\"raspberry\"));\n        assert_eq!(fav_fruits_iterator.next(), todo!()); // TODO: Replace `todo!()`\n    }\n}\n"
  },
  {
    "path": "exercises/18_iterators/iterators2.rs",
    "content": "// In this exercise, you'll learn some of the unique advantages that iterators\n// can offer.\n\n// TODO: Complete the `capitalize_first` function.\n// \"hello\" -> \"Hello\"\nfn capitalize_first(input: &str) -> String {\n    let mut chars = input.chars();\n    match chars.next() {\n        None => String::new(),\n        Some(first) => todo!(),\n    }\n}\n\n// TODO: Apply the `capitalize_first` function to a slice of string slices.\n// Return a vector of strings.\n// [\"hello\", \"world\"] -> [\"Hello\", \"World\"]\nfn capitalize_words_vector(words: &[&str]) -> Vec<String> {\n    // ???\n}\n\n// TODO: Apply the `capitalize_first` function again to a slice of string\n// slices. Return a single string.\n// [\"hello\", \" \", \"world\"] -> \"Hello World\"\nfn capitalize_words_string(words: &[&str]) -> String {\n    // ???\n}\n\nfn main() {\n    // You can optionally experiment here.\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    #[test]\n    fn test_success() {\n        assert_eq!(capitalize_first(\"hello\"), \"Hello\");\n    }\n\n    #[test]\n    fn test_empty() {\n        assert_eq!(capitalize_first(\"\"), \"\");\n    }\n\n    #[test]\n    fn test_iterate_string_vec() {\n        let words = vec![\"hello\", \"world\"];\n        assert_eq!(capitalize_words_vector(&words), [\"Hello\", \"World\"]);\n    }\n\n    #[test]\n    fn test_iterate_into_string() {\n        let words = vec![\"hello\", \" \", \"world\"];\n        assert_eq!(capitalize_words_string(&words), \"Hello World\");\n    }\n}\n"
  },
  {
    "path": "exercises/18_iterators/iterators3.rs",
    "content": "#[derive(Debug, PartialEq, Eq)]\nenum DivisionError {\n    // Example: 42 / 0\n    DivideByZero,\n    // Only case for `i64`: `i64::MIN / -1` because the result is `i64::MAX + 1`\n    IntegerOverflow,\n    // Example: 5 / 2 = 2.5\n    NotDivisible,\n}\n\n// TODO: Calculate `a` divided by `b` if `a` is evenly divisible by `b`.\n// Otherwise, return a suitable error.\nfn divide(a: i64, b: i64) -> Result<i64, DivisionError> {\n    todo!();\n}\n\n// TODO: Add the correct return type and complete the function body.\n// Desired output: `Ok([1, 11, 1426, 3])`\nfn result_with_list() {\n    let numbers = [27, 297, 38502, 81];\n    let division_results = numbers.into_iter().map(|n| divide(n, 27));\n}\n\n// TODO: Add the correct return type and complete the function body.\n// Desired output: `[Ok(1), Ok(11), Ok(1426), Ok(3)]`\nfn list_of_results() {\n    let numbers = [27, 297, 38502, 81];\n    let division_results = numbers.into_iter().map(|n| divide(n, 27));\n}\n\nfn main() {\n    // You can optionally experiment here.\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    #[test]\n    fn test_success() {\n        assert_eq!(divide(81, 9), Ok(9));\n        assert_eq!(divide(81, -1), Ok(-81));\n        assert_eq!(divide(i64::MIN, i64::MIN), Ok(1));\n    }\n\n    #[test]\n    fn test_divide_by_0() {\n        assert_eq!(divide(81, 0), Err(DivisionError::DivideByZero));\n    }\n\n    #[test]\n    fn test_integer_overflow() {\n        assert_eq!(divide(i64::MIN, -1), Err(DivisionError::IntegerOverflow));\n    }\n\n    #[test]\n    fn test_not_divisible() {\n        assert_eq!(divide(81, 6), Err(DivisionError::NotDivisible));\n    }\n\n    #[test]\n    fn test_divide_0_by_something() {\n        assert_eq!(divide(0, 81), Ok(0));\n    }\n\n    #[test]\n    fn test_result_with_list() {\n        assert_eq!(result_with_list().unwrap(), [1, 11, 1426, 3]);\n    }\n\n    #[test]\n    fn test_list_of_results() {\n        assert_eq!(list_of_results(), [Ok(1), Ok(11), Ok(1426), Ok(3)]);\n    }\n}\n"
  },
  {
    "path": "exercises/18_iterators/iterators4.rs",
    "content": "fn factorial(num: u64) -> u64 {\n    // TODO: Complete this function to return the factorial of `num` which is\n    // defined as `1 * 2 * 3 * … * num`.\n    // https://en.wikipedia.org/wiki/Factorial\n    //\n    // Do not use:\n    // - early returns (using the `return` keyword explicitly)\n    // Try not to use:\n    // - imperative style loops (for/while)\n    // - additional variables\n    // For an extra challenge, don't use:\n    // - recursion\n}\n\nfn main() {\n    // You can optionally experiment here.\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    #[test]\n    fn factorial_of_0() {\n        assert_eq!(factorial(0), 1);\n    }\n\n    #[test]\n    fn factorial_of_1() {\n        assert_eq!(factorial(1), 1);\n    }\n    #[test]\n    fn factorial_of_2() {\n        assert_eq!(factorial(2), 2);\n    }\n\n    #[test]\n    fn factorial_of_4() {\n        assert_eq!(factorial(4), 24);\n    }\n}\n"
  },
  {
    "path": "exercises/18_iterators/iterators5.rs",
    "content": "// Let's define a simple model to track Rustlings' exercise progress. Progress\n// will be modelled using a hash map. The name of the exercise is the key and\n// the progress is the value. Two counting functions were created to count the\n// number of exercises with a given progress. Recreate this counting\n// functionality using iterators. Try to not use imperative loops (for/while).\n\nuse std::collections::HashMap;\n\n#[derive(Clone, Copy, PartialEq, Eq)]\nenum Progress {\n    None,\n    Some,\n    Complete,\n}\n\nfn count_for(map: &HashMap<String, Progress>, value: Progress) -> usize {\n    let mut count = 0;\n    for val in map.values() {\n        if *val == value {\n            count += 1;\n        }\n    }\n    count\n}\n\n// TODO: Implement the functionality of `count_for` but with an iterator instead\n// of a `for` loop.\nfn count_iterator(map: &HashMap<String, Progress>, value: Progress) -> usize {\n    // `map` is a hash map with `String` keys and `Progress` values.\n    // map = { \"variables1\": Complete, \"from_str\": None, … }\n}\n\nfn count_collection_for(collection: &[HashMap<String, Progress>], value: Progress) -> usize {\n    let mut count = 0;\n    for map in collection {\n        for val in map.values() {\n            if *val == value {\n                count += 1;\n            }\n        }\n    }\n    count\n}\n\n// TODO: Implement the functionality of `count_collection_for` but with an\n// iterator instead of a `for` loop.\nfn count_collection_iterator(collection: &[HashMap<String, Progress>], value: Progress) -> usize {\n    // `collection` is a slice of hash maps.\n    // collection = [{ \"variables1\": Complete, \"from_str\": None, … },\n    //               { \"variables2\": Complete, … }, … ]\n}\n\nfn main() {\n    // You can optionally experiment here.\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    fn get_map() -> HashMap<String, Progress> {\n        use Progress::*;\n\n        let mut map = HashMap::new();\n        map.insert(String::from(\"variables1\"), Complete);\n        map.insert(String::from(\"functions1\"), Complete);\n        map.insert(String::from(\"hashmap1\"), Complete);\n        map.insert(String::from(\"arc1\"), Some);\n        map.insert(String::from(\"as_ref_mut\"), None);\n        map.insert(String::from(\"from_str\"), None);\n\n        map\n    }\n\n    fn get_vec_map() -> Vec<HashMap<String, Progress>> {\n        use Progress::*;\n\n        let map = get_map();\n\n        let mut other = HashMap::new();\n        other.insert(String::from(\"variables2\"), Complete);\n        other.insert(String::from(\"functions2\"), Complete);\n        other.insert(String::from(\"if1\"), Complete);\n        other.insert(String::from(\"from_into\"), None);\n        other.insert(String::from(\"try_from_into\"), None);\n\n        vec![map, other]\n    }\n\n    #[test]\n    fn count_complete() {\n        let map = get_map();\n        assert_eq!(count_iterator(&map, Progress::Complete), 3);\n    }\n\n    #[test]\n    fn count_some() {\n        let map = get_map();\n        assert_eq!(count_iterator(&map, Progress::Some), 1);\n    }\n\n    #[test]\n    fn count_none() {\n        let map = get_map();\n        assert_eq!(count_iterator(&map, Progress::None), 2);\n    }\n\n    #[test]\n    fn count_complete_equals_for() {\n        let map = get_map();\n        let progress_states = [Progress::Complete, Progress::Some, Progress::None];\n        for progress_state in progress_states {\n            assert_eq!(\n                count_for(&map, progress_state),\n                count_iterator(&map, progress_state),\n            );\n        }\n    }\n\n    #[test]\n    fn count_collection_complete() {\n        let collection = get_vec_map();\n        assert_eq!(\n            count_collection_iterator(&collection, Progress::Complete),\n            6,\n        );\n    }\n\n    #[test]\n    fn count_collection_some() {\n        let collection = get_vec_map();\n        assert_eq!(count_collection_iterator(&collection, Progress::Some), 1);\n    }\n\n    #[test]\n    fn count_collection_none() {\n        let collection = get_vec_map();\n        assert_eq!(count_collection_iterator(&collection, Progress::None), 4);\n    }\n\n    #[test]\n    fn count_collection_equals_for() {\n        let collection = get_vec_map();\n        let progress_states = [Progress::Complete, Progress::Some, Progress::None];\n\n        for progress_state in progress_states {\n            assert_eq!(\n                count_collection_for(&collection, progress_state),\n                count_collection_iterator(&collection, progress_state),\n            );\n        }\n    }\n}\n"
  },
  {
    "path": "exercises/19_smart_pointers/README.md",
    "content": "# Smart Pointers\n\nIn Rust, smart pointers are variables that contain an address in memory and reference some other data, but they also have additional metadata and capabilities.\nSmart pointers in Rust often own the data they point to, while references only borrow data.\n\n## Further Information\n\n- [Smart Pointers](https://doc.rust-lang.org/book/ch15-00-smart-pointers.html)\n- [Using Box to Point to Data on the Heap](https://doc.rust-lang.org/book/ch15-01-box.html)\n- [Rc\\<T\\>, the Reference Counted Smart Pointer](https://doc.rust-lang.org/book/ch15-04-rc.html)\n- [Shared-State Concurrency](https://doc.rust-lang.org/book/ch16-03-shared-state.html)\n- [Cow Documentation](https://doc.rust-lang.org/std/borrow/enum.Cow.html)\n"
  },
  {
    "path": "exercises/19_smart_pointers/arc1.rs",
    "content": "// In this exercise, we are given a `Vec` of `u32` called `numbers` with values\n// ranging from 0 to 99. We would like to use this set of numbers within 8\n// different threads simultaneously. Each thread is going to get the sum of\n// every eighth value with an offset.\n//\n// The first thread (offset 0), will sum 0, 8, 16, …\n// The second thread (offset 1), will sum 1, 9, 17, …\n// The third thread (offset 2), will sum 2, 10, 18, …\n// …\n// The eighth thread (offset 7), will sum 7, 15, 23, …\n//\n// Each thread should own a reference-counting pointer to the vector of\n// numbers. But `Rc` isn't thread-safe. Therefore, we need to use `Arc`.\n//\n// Don't get distracted by how threads are spawned and joined. We will practice\n// that later in the exercises about threads.\n\n// Don't change the lines below.\n#![forbid(unused_imports)]\nuse std::{sync::Arc, thread};\n\nfn main() {\n    let numbers: Vec<_> = (0..100u32).collect();\n\n    // TODO: Define `shared_numbers` by using `Arc`.\n    // let shared_numbers = ???;\n\n    let mut join_handles = Vec::new();\n\n    for offset in 0..8 {\n        // TODO: Define `child_numbers` using `shared_numbers`.\n        // let child_numbers = ???;\n\n        let handle = thread::spawn(move || {\n            let sum: u32 = child_numbers.iter().filter(|&&n| n % 8 == offset).sum();\n            println!(\"Sum of offset {offset} is {sum}\");\n        });\n\n        join_handles.push(handle);\n    }\n\n    for handle in join_handles.into_iter() {\n        handle.join().unwrap();\n    }\n}\n"
  },
  {
    "path": "exercises/19_smart_pointers/box1.rs",
    "content": "// At compile time, Rust needs to know how much space a type takes up. This\n// becomes problematic for recursive types, where a value can have as part of\n// itself another value of the same type. To get around the issue, we can use a\n// `Box` - a smart pointer used to store data on the heap, which also allows us\n// to wrap a recursive type.\n//\n// The recursive type we're implementing in this exercise is the \"cons list\", a\n// data structure frequently found in functional programming languages. Each\n// item in a cons list contains two elements: The value of the current item and\n// the next item. The last item is a value called `Nil`.\n\n// TODO: Use a `Box` in the enum definition to make the code compile.\n#[derive(PartialEq, Debug)]\nenum List {\n    Cons(i32, List),\n    Nil,\n}\n\n// TODO: Create an empty cons list.\nfn create_empty_list() -> List {\n    todo!()\n}\n\n// TODO: Create a non-empty cons list.\nfn create_non_empty_list() -> List {\n    todo!()\n}\n\nfn main() {\n    println!(\"This is an empty cons list: {:?}\", create_empty_list());\n    println!(\n        \"This is a non-empty cons list: {:?}\",\n        create_non_empty_list(),\n    );\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    #[test]\n    fn test_create_empty_list() {\n        assert_eq!(create_empty_list(), List::Nil);\n    }\n\n    #[test]\n    fn test_create_non_empty_list() {\n        assert_ne!(create_empty_list(), create_non_empty_list());\n    }\n}\n"
  },
  {
    "path": "exercises/19_smart_pointers/cow1.rs",
    "content": "// This exercise explores the `Cow` (Clone-On-Write) smart pointer. It can\n// enclose and provide immutable access to borrowed data and clone the data\n// lazily when mutation or ownership is required. The type is designed to work\n// with general borrowed data via the `Borrow` trait.\n\nuse std::borrow::Cow;\n\nfn abs_all(input: &mut Cow<[i32]>) {\n    for ind in 0..input.len() {\n        let value = input[ind];\n        if value < 0 {\n            // Clones into a vector if not already owned.\n            input.to_mut()[ind] = -value;\n        }\n    }\n}\n\nfn main() {\n    // You can optionally experiment here.\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    #[test]\n    fn reference_mutation() {\n        // Clone occurs because `input` needs to be mutated.\n        let vec = vec![-1, 0, 1];\n        let mut input = Cow::from(&vec);\n        abs_all(&mut input);\n        assert!(matches!(input, Cow::Owned(_)));\n    }\n\n    #[test]\n    fn reference_no_mutation() {\n        // No clone occurs because `input` doesn't need to be mutated.\n        let vec = vec![0, 1, 2];\n        let mut input = Cow::from(&vec);\n        abs_all(&mut input);\n        // TODO: Replace `todo!()` with `Cow::Owned(_)` or `Cow::Borrowed(_)`.\n        assert!(matches!(input, todo!()));\n    }\n\n    #[test]\n    fn owned_no_mutation() {\n        // We can also pass `vec` without `&` so `Cow` owns it directly. In this\n        // case, no mutation occurs (all numbers are already absolute) and thus\n        // also no clone. But the result is still owned because it was never\n        // borrowed or mutated.\n        let vec = vec![0, 1, 2];\n        let mut input = Cow::from(vec);\n        abs_all(&mut input);\n        // TODO: Replace `todo!()` with `Cow::Owned(_)` or `Cow::Borrowed(_)`.\n        assert!(matches!(input, todo!()));\n    }\n\n    #[test]\n    fn owned_mutation() {\n        // Of course this is also the case if a mutation does occur (not all\n        // numbers are absolute). In this case, the call to `to_mut()` in the\n        // `abs_all` function returns a reference to the same data as before.\n        let vec = vec![-1, 0, 1];\n        let mut input = Cow::from(vec);\n        abs_all(&mut input);\n        // TODO: Replace `todo!()` with `Cow::Owned(_)` or `Cow::Borrowed(_)`.\n        assert!(matches!(input, todo!()));\n    }\n}\n"
  },
  {
    "path": "exercises/19_smart_pointers/rc1.rs",
    "content": "// In this exercise, we want to express the concept of multiple owners via the\n// `Rc<T>` type. This is a model of our solar system - there is a `Sun` type and\n// multiple `Planet`s. The planets take ownership of the sun, indicating that\n// they revolve around the sun.\n\nuse std::rc::Rc;\n\n#[derive(Debug)]\nstruct Sun;\n\n#[derive(Debug)]\nenum Planet {\n    Mercury(Rc<Sun>),\n    Venus(Rc<Sun>),\n    Earth(Rc<Sun>),\n    Mars(Rc<Sun>),\n    Jupiter(Rc<Sun>),\n    Saturn(Rc<Sun>),\n    Uranus(Rc<Sun>),\n    Neptune(Rc<Sun>),\n}\n\nimpl Planet {\n    fn details(&self) {\n        println!(\"Hi from {self:?}!\");\n    }\n}\n\nfn main() {\n    // You can optionally experiment here.\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    #[test]\n    fn rc1() {\n        let sun = Rc::new(Sun);\n        println!(\"reference count = {}\", Rc::strong_count(&sun)); // 1 reference\n\n        let mercury = Planet::Mercury(Rc::clone(&sun));\n        println!(\"reference count = {}\", Rc::strong_count(&sun)); // 2 references\n        mercury.details();\n\n        let venus = Planet::Venus(Rc::clone(&sun));\n        println!(\"reference count = {}\", Rc::strong_count(&sun)); // 3 references\n        venus.details();\n\n        let earth = Planet::Earth(Rc::clone(&sun));\n        println!(\"reference count = {}\", Rc::strong_count(&sun)); // 4 references\n        earth.details();\n\n        let mars = Planet::Mars(Rc::clone(&sun));\n        println!(\"reference count = {}\", Rc::strong_count(&sun)); // 5 references\n        mars.details();\n\n        let jupiter = Planet::Jupiter(Rc::clone(&sun));\n        println!(\"reference count = {}\", Rc::strong_count(&sun)); // 6 references\n        jupiter.details();\n\n        // TODO\n        let saturn = Planet::Saturn(Rc::new(Sun));\n        println!(\"reference count = {}\", Rc::strong_count(&sun)); // 7 references\n        saturn.details();\n\n        // TODO\n        let uranus = Planet::Uranus(Rc::new(Sun));\n        println!(\"reference count = {}\", Rc::strong_count(&sun)); // 8 references\n        uranus.details();\n\n        // TODO\n        let neptune = Planet::Neptune(Rc::new(Sun));\n        println!(\"reference count = {}\", Rc::strong_count(&sun)); // 9 references\n        neptune.details();\n\n        assert_eq!(Rc::strong_count(&sun), 9);\n\n        drop(neptune);\n        println!(\"reference count = {}\", Rc::strong_count(&sun)); // 8 references\n\n        drop(uranus);\n        println!(\"reference count = {}\", Rc::strong_count(&sun)); // 7 references\n\n        drop(saturn);\n        println!(\"reference count = {}\", Rc::strong_count(&sun)); // 6 references\n\n        drop(jupiter);\n        println!(\"reference count = {}\", Rc::strong_count(&sun)); // 5 references\n\n        drop(mars);\n        println!(\"reference count = {}\", Rc::strong_count(&sun)); // 4 references\n\n        // TODO\n        println!(\"reference count = {}\", Rc::strong_count(&sun)); // 3 references\n\n        // TODO\n        println!(\"reference count = {}\", Rc::strong_count(&sun)); // 2 references\n\n        // TODO\n        println!(\"reference count = {}\", Rc::strong_count(&sun)); // 1 reference\n\n        assert_eq!(Rc::strong_count(&sun), 1);\n    }\n}\n"
  },
  {
    "path": "exercises/20_threads/README.md",
    "content": "# Threads\n\nIn most current operating systems, an executed program's code is run in a process, and the operating system manages multiple processes at once.\nWithin your program, you can also have independent parts that run simultaneously. The features that run these independent parts are called threads.\n\n## Further information\n\n- [Dining Philosophers example](https://doc.rust-lang.org/1.4.0/book/dining-philosophers.html)\n- [Using Threads to Run Code Simultaneously](https://doc.rust-lang.org/book/ch16-01-threads.html)\n- [Using Message Passing to Transfer Data Between Threads](https://doc.rust-lang.org/book/ch16-02-message-passing.html)\n"
  },
  {
    "path": "exercises/20_threads/threads1.rs",
    "content": "// This program spawns multiple threads that each runs for at least 250ms, and\n// each thread returns how much time it took to complete. The program should\n// wait until all the spawned threads have finished and should collect their\n// return values into a vector.\n\nuse std::{\n    thread,\n    time::{Duration, Instant},\n};\n\nfn main() {\n    let mut handles = Vec::new();\n    for i in 0..10 {\n        let handle = thread::spawn(move || {\n            let start = Instant::now();\n            thread::sleep(Duration::from_millis(250));\n            println!(\"Thread {i} done\");\n            start.elapsed().as_millis()\n        });\n        handles.push(handle);\n    }\n\n    let mut results = Vec::new();\n    for handle in handles {\n        // TODO: Collect the results of all threads into the `results` vector.\n        // Use the `JoinHandle` struct which is returned by `thread::spawn`.\n    }\n\n    if results.len() != 10 {\n        panic!(\"Oh no! Some thread isn't done yet!\");\n    }\n\n    println!();\n    for (i, result) in results.into_iter().enumerate() {\n        println!(\"Thread {i} took {result}ms\");\n    }\n}\n"
  },
  {
    "path": "exercises/20_threads/threads2.rs",
    "content": "// Building on the last exercise, we want all of the threads to complete their\n// work. But this time, the spawned threads need to be in charge of updating a\n// shared value: `JobStatus.jobs_done`\n\nuse std::{sync::Arc, thread, time::Duration};\n\nstruct JobStatus {\n    jobs_done: u32,\n}\n\nfn main() {\n    // TODO: `Arc` isn't enough if you want a **mutable** shared state.\n    let status = Arc::new(JobStatus { jobs_done: 0 });\n\n    let mut handles = Vec::new();\n    for _ in 0..10 {\n        let status_shared = Arc::clone(&status);\n        let handle = thread::spawn(move || {\n            thread::sleep(Duration::from_millis(250));\n\n            // TODO: You must take an action before you update a shared value.\n            status_shared.jobs_done += 1;\n        });\n        handles.push(handle);\n    }\n\n    // Waiting for all jobs to complete.\n    for handle in handles {\n        handle.join().unwrap();\n    }\n\n    // TODO: Print the value of `JobStatus.jobs_done`.\n    println!(\"Jobs done: {}\", todo!());\n}\n"
  },
  {
    "path": "exercises/20_threads/threads3.rs",
    "content": "use std::{sync::mpsc, thread, time::Duration};\n\nstruct Queue {\n    first_half: Vec<u32>,\n    second_half: Vec<u32>,\n}\n\nimpl Queue {\n    fn new() -> Self {\n        Self {\n            first_half: vec![1, 2, 3, 4, 5],\n            second_half: vec![6, 7, 8, 9, 10],\n        }\n    }\n}\n\nfn send_tx(q: Queue, tx: mpsc::Sender<u32>) {\n    // TODO: We want to send `tx` to both threads. But currently, it is moved\n    // into the first thread. How could you solve this problem?\n    thread::spawn(move || {\n        for val in q.first_half {\n            println!(\"Sending {val:?}\");\n            tx.send(val).unwrap();\n            thread::sleep(Duration::from_millis(250));\n        }\n    });\n\n    thread::spawn(move || {\n        for val in q.second_half {\n            println!(\"Sending {val:?}\");\n            tx.send(val).unwrap();\n            thread::sleep(Duration::from_millis(250));\n        }\n    });\n}\n\nfn main() {\n    // You can optionally experiment here.\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    #[test]\n    fn threads3() {\n        let (tx, rx) = mpsc::channel();\n        let queue = Queue::new();\n\n        send_tx(queue, tx);\n\n        let mut received = Vec::with_capacity(10);\n        for value in rx {\n            received.push(value);\n        }\n\n        received.sort();\n        assert_eq!(received, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);\n    }\n}\n"
  },
  {
    "path": "exercises/21_macros/README.md",
    "content": "# Macros\n\nRust's macro system is very powerful, but also kind of difficult to wrap your\nhead around. We're not going to teach you how to write your own fully-featured\nmacros. Instead, we'll show you how to use and create them.\n\nIf you'd like to learn more about writing your own macros, the\n[macrokata](https://github.com/tfpk/macrokata) project has a similar style\nof exercises to Rustlings, but is all about learning to write Macros.\n\n## Further information\n\n- [The Rust Book - Macros](https://doc.rust-lang.org/book/ch20-05-macros.html)\n- [The Little Book of Rust Macros](https://veykril.github.io/tlborm/)\n- [Rust by Example - macro_rules!](https://doc.rust-lang.org/rust-by-example/macros.html)\n"
  },
  {
    "path": "exercises/21_macros/macros1.rs",
    "content": "macro_rules! my_macro {\n    () => {\n        println!(\"Check out my macro!\");\n    };\n}\n\nfn main() {\n    // TODO: Fix the macro call.\n    my_macro();\n}\n"
  },
  {
    "path": "exercises/21_macros/macros2.rs",
    "content": "fn main() {\n    my_macro!();\n}\n\n// TODO: Fix the compiler error by moving the whole definition of this macro.\nmacro_rules! my_macro {\n    () => {\n        println!(\"Check out my macro!\");\n    };\n}\n"
  },
  {
    "path": "exercises/21_macros/macros3.rs",
    "content": "// TODO: Fix the compiler error without taking the macro definition out of this\n// module.\nmod macros {\n    macro_rules! my_macro {\n        () => {\n            println!(\"Check out my macro!\");\n        };\n    }\n}\n\nfn main() {\n    my_macro!();\n}\n"
  },
  {
    "path": "exercises/21_macros/macros4.rs",
    "content": "// TODO: Fix the compiler error by adding one or two characters.\n#[rustfmt::skip]\nmacro_rules! my_macro {\n    () => {\n        println!(\"Check out my macro!\");\n    }\n    ($val:expr) => {\n        println!(\"Look at this other macro: {}\", $val);\n    }\n}\n\nfn main() {\n    my_macro!();\n    my_macro!(7777);\n}\n"
  },
  {
    "path": "exercises/22_clippy/README.md",
    "content": "# Clippy\n\nThe Clippy tool is a collection of lints to analyze your code so you can catch common mistakes and improve your Rust code.\n\nIf you used the installation script for Rustlings, Clippy should be already installed.\nIf not you can install it manually via `rustup component add clippy`.\n\n## Further information\n\n- [GitHub Repository](https://github.com/rust-lang/rust-clippy).\n"
  },
  {
    "path": "exercises/22_clippy/clippy1.rs",
    "content": "// The Clippy tool is a collection of lints to analyze your code so you can\n// catch common mistakes and improve your Rust code.\n//\n// For these exercises, the code will fail to compile when there are Clippy\n// warnings. Check Clippy's suggestions from the output to solve the exercise.\n\nfn main() {\n    // TODO: Fix the Clippy lint in this line.\n    let pi = 3.14;\n    let radius: f32 = 5.0;\n\n    let area = pi * radius.powi(2);\n\n    println!(\"The area of a circle with radius {radius:.2} is {area:.5}\");\n}\n"
  },
  {
    "path": "exercises/22_clippy/clippy2.rs",
    "content": "fn main() {\n    let mut res = 42;\n    let option = Some(12);\n    // TODO: Fix the Clippy lint.\n    for x in option {\n        res += x;\n    }\n\n    println!(\"{res}\");\n}\n"
  },
  {
    "path": "exercises/22_clippy/clippy3.rs",
    "content": "// Here are some more easy Clippy fixes so you can see its utility.\n// TODO: Fix all the Clippy lints.\n\n#[allow(unused_variables, unused_assignments)]\nfn main() {\n    let my_option: Option<&str> = None;\n    // Assume that you don't know the value of `my_option`.\n    // In the case of `Some`, we want to print its value.\n    if my_option.is_none() {\n        println!(\"{}\", my_option.unwrap());\n    }\n\n    #[rustfmt::skip]\n    let my_arr = &[\n        -1, -2, -3\n        -4, -5, -6\n    ];\n    println!(\"My array! Here it is: {my_arr:?}\");\n\n    let mut my_vec = vec![1, 2, 3, 4, 5];\n    my_vec.resize(0, 5);\n    println!(\"This Vec is empty, see? {my_vec:?}\");\n\n    let mut value_a = 45;\n    let mut value_b = 66;\n    // Let's swap these two!\n    value_a = value_b;\n    value_b = value_a;\n    println!(\"value a: {value_a}; value b: {value_b}\");\n}\n"
  },
  {
    "path": "exercises/23_conversions/README.md",
    "content": "# Type conversions\n\nRust offers a multitude of ways to convert a value of a given type into another type.\n\nThe simplest form of type conversion is a type cast expression. It is denoted with the binary operator `as`. For instance, `println!(\"{}\", 1 + 1.0);` would not compile, since `1` is an integer while `1.0` is a float. However, `println!(\"{}\", 1 as f32 + 1.0)` should compile. The exercise [`using_as`](using_as.rs) tries to cover this.\n\nRust 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.\nThe traits are the following:\n\n- `From` and `Into` covered in [`from_into`](from_into.rs)\n- `TryFrom` and `TryInto` covered in [`try_from_into`](try_from_into.rs)\n- `AsRef` and `AsMut` covered in [`as_ref_mut`](as_ref_mut.rs)\n\nFurthermore, the `std::str` module offers a trait called [`FromStr`](https://doc.rust-lang.org/std/str/trait.FromStr.html) which helps with converting strings into target types via the `parse` method on strings. If properly implemented for a given type `Person`, then `let p: Person = \"Mark,20\".parse().unwrap()` should both compile and run without panicking.\n\nThese should be the main ways ***within the standard library*** to convert data into your desired types.\n\n## Further information\n\nThese are not directly covered in the book, but the standard library has a great documentation for it.\n\n- [conversions](https://doc.rust-lang.org/std/convert/index.html)\n- [`FromStr` trait](https://doc.rust-lang.org/std/str/trait.FromStr.html)\n"
  },
  {
    "path": "exercises/23_conversions/as_ref_mut.rs",
    "content": "// AsRef and AsMut allow for cheap reference-to-reference conversions. Read more\n// about them at https://doc.rust-lang.org/std/convert/trait.AsRef.html and\n// https://doc.rust-lang.org/std/convert/trait.AsMut.html, respectively.\n\n// Obtain the number of bytes (not characters) in the given argument\n// (`.len()` returns the number of bytes in a string).\n// TODO: Add the `AsRef` trait appropriately as a trait bound.\nfn byte_counter<T>(arg: T) -> usize {\n    arg.as_ref().len()\n}\n\n// Obtain the number of characters (not bytes) in the given argument.\n// TODO: Add the `AsRef` trait appropriately as a trait bound.\nfn char_counter<T>(arg: T) -> usize {\n    arg.as_ref().chars().count()\n}\n\n// Squares a number using `as_mut()`.\n// TODO: Add the appropriate trait bound.\nfn num_sq<T>(arg: &mut T) {\n    // TODO: Implement the function body.\n}\n\nfn main() {\n    // You can optionally experiment here.\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    #[test]\n    fn different_counts() {\n        let s = \"Café au lait\";\n        assert_ne!(char_counter(s), byte_counter(s));\n    }\n\n    #[test]\n    fn same_counts() {\n        let s = \"Cafe au lait\";\n        assert_eq!(char_counter(s), byte_counter(s));\n    }\n\n    #[test]\n    fn different_counts_using_string() {\n        let s = String::from(\"Café au lait\");\n        assert_ne!(char_counter(s.clone()), byte_counter(s));\n    }\n\n    #[test]\n    fn same_counts_using_string() {\n        let s = String::from(\"Cafe au lait\");\n        assert_eq!(char_counter(s.clone()), byte_counter(s));\n    }\n\n    #[test]\n    fn mut_box() {\n        let mut num: Box<u32> = Box::new(3);\n        num_sq(&mut num);\n        assert_eq!(*num, 9);\n    }\n}\n"
  },
  {
    "path": "exercises/23_conversions/from_into.rs",
    "content": "// The `From` trait is used for value-to-value conversions. If `From` is\n// implemented, an implementation of `Into` is automatically provided.\n// You can read more about it in the documentation:\n// https://doc.rust-lang.org/std/convert/trait.From.html\n\n#[derive(Debug)]\nstruct Person {\n    name: String,\n    age: u8,\n}\n\n// We implement the Default trait to use it as a fallback when the provided\n// string is not convertible into a `Person` object.\nimpl Default for Person {\n    fn default() -> Self {\n        Self {\n            name: String::from(\"John\"),\n            age: 30,\n        }\n    }\n}\n\n// TODO: Complete this `From` implementation to be able to parse a `Person`\n// out of a string in the form of \"Mark,20\".\n// Note that you'll need to parse the age component into a `u8` with something\n// like `\"4\".parse::<u8>()`.\n//\n// Steps:\n// 1. Split the given string on the commas present in it.\n// 2. If the split operation returns less or more than 2 elements, return the\n//    default of `Person`.\n// 3. Use the first element from the split operation as the name.\n// 4. If the name is empty, return the default of `Person`.\n// 5. Parse the second element from the split operation into a `u8` as the age.\n// 6. If parsing the age fails, return the default of `Person`.\nimpl From<&str> for Person {\n    fn from(s: &str) -> Self {}\n}\n\nfn main() {\n    // Use the `from` function.\n    let p1 = Person::from(\"Mark,20\");\n    println!(\"{p1:?}\");\n\n    // Since `From` is implemented for Person, we are able to use `Into`.\n    let p2: Person = \"Gerald,70\".into();\n    println!(\"{p2:?}\");\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    #[test]\n    fn test_default() {\n        let dp = Person::default();\n        assert_eq!(dp.name, \"John\");\n        assert_eq!(dp.age, 30);\n    }\n\n    #[test]\n    fn test_bad_convert() {\n        let p = Person::from(\"\");\n        assert_eq!(p.name, \"John\");\n        assert_eq!(p.age, 30);\n    }\n\n    #[test]\n    fn test_good_convert() {\n        let p = Person::from(\"Mark,20\");\n        assert_eq!(p.name, \"Mark\");\n        assert_eq!(p.age, 20);\n    }\n\n    #[test]\n    fn test_bad_age() {\n        let p = Person::from(\"Mark,twenty\");\n        assert_eq!(p.name, \"John\");\n        assert_eq!(p.age, 30);\n    }\n\n    #[test]\n    fn test_missing_comma_and_age() {\n        let p: Person = Person::from(\"Mark\");\n        assert_eq!(p.name, \"John\");\n        assert_eq!(p.age, 30);\n    }\n\n    #[test]\n    fn test_missing_age() {\n        let p: Person = Person::from(\"Mark,\");\n        assert_eq!(p.name, \"John\");\n        assert_eq!(p.age, 30);\n    }\n\n    #[test]\n    fn test_missing_name() {\n        let p: Person = Person::from(\",1\");\n        assert_eq!(p.name, \"John\");\n        assert_eq!(p.age, 30);\n    }\n\n    #[test]\n    fn test_missing_name_and_age() {\n        let p: Person = Person::from(\",\");\n        assert_eq!(p.name, \"John\");\n        assert_eq!(p.age, 30);\n    }\n\n    #[test]\n    fn test_missing_name_and_invalid_age() {\n        let p: Person = Person::from(\",one\");\n        assert_eq!(p.name, \"John\");\n        assert_eq!(p.age, 30);\n    }\n\n    #[test]\n    fn test_trailing_comma() {\n        let p: Person = Person::from(\"Mike,32,\");\n        assert_eq!(p.name, \"John\");\n        assert_eq!(p.age, 30);\n    }\n\n    #[test]\n    fn test_trailing_comma_and_some_string() {\n        let p: Person = Person::from(\"Mike,32,dog\");\n        assert_eq!(p.name, \"John\");\n        assert_eq!(p.age, 30);\n    }\n}\n"
  },
  {
    "path": "exercises/23_conversions/from_str.rs",
    "content": "// This is similar to the previous `from_into` exercise. But this time, we'll\n// implement `FromStr` and return errors instead of falling back to a default\n// value. Additionally, upon implementing `FromStr`, you can use the `parse`\n// method on strings to generate an object of the implementor type. You can read\n// more about it in the documentation:\n// https://doc.rust-lang.org/std/str/trait.FromStr.html\n\nuse std::num::ParseIntError;\nuse std::str::FromStr;\n\n#[derive(Debug, PartialEq)]\nstruct Person {\n    name: String,\n    age: u8,\n}\n\n// We will use this error type for the `FromStr` implementation.\n#[derive(Debug, PartialEq)]\nenum ParsePersonError {\n    // Incorrect number of fields\n    BadLen,\n    // Empty name field\n    NoName,\n    // Wrapped error from parse::<u8>()\n    ParseInt(ParseIntError),\n}\n\n// TODO: Complete this `FromStr` implementation to be able to parse a `Person`\n// out of a string in the form of \"Mark,20\".\n// Note that you'll need to parse the age component into a `u8` with something\n// like `\"4\".parse::<u8>()`.\n//\n// Steps:\n// 1. Split the given string on the commas present in it.\n// 2. If the split operation returns less or more than 2 elements, return the\n//    error `ParsePersonError::BadLen`.\n// 3. Use the first element from the split operation as the name.\n// 4. If the name is empty, return the error `ParsePersonError::NoName`.\n// 5. Parse the second element from the split operation into a `u8` as the age.\n// 6. If parsing the age fails, return the error `ParsePersonError::ParseInt`.\nimpl FromStr for Person {\n    type Err = ParsePersonError;\n\n    fn from_str(s: &str) -> Result<Self, Self::Err> {}\n}\n\nfn main() {\n    let p = \"Mark,20\".parse::<Person>();\n    println!(\"{p:?}\");\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n    use ParsePersonError::*;\n\n    #[test]\n    fn empty_input() {\n        assert_eq!(\"\".parse::<Person>(), Err(BadLen));\n    }\n\n    #[test]\n    fn good_input() {\n        let p = \"John,32\".parse::<Person>();\n        assert!(p.is_ok());\n        let p = p.unwrap();\n        assert_eq!(p.name, \"John\");\n        assert_eq!(p.age, 32);\n    }\n\n    #[test]\n    fn missing_age() {\n        assert!(matches!(\"John,\".parse::<Person>(), Err(ParseInt(_))));\n    }\n\n    #[test]\n    fn invalid_age() {\n        assert!(matches!(\"John,twenty\".parse::<Person>(), Err(ParseInt(_))));\n    }\n\n    #[test]\n    fn missing_comma_and_age() {\n        assert_eq!(\"John\".parse::<Person>(), Err(BadLen));\n    }\n\n    #[test]\n    fn missing_name() {\n        assert_eq!(\",1\".parse::<Person>(), Err(NoName));\n    }\n\n    #[test]\n    fn missing_name_and_age() {\n        assert!(matches!(\",\".parse::<Person>(), Err(NoName | ParseInt(_))));\n    }\n\n    #[test]\n    fn missing_name_and_invalid_age() {\n        assert!(matches!(\n            \",one\".parse::<Person>(),\n            Err(NoName | ParseInt(_)),\n        ));\n    }\n\n    #[test]\n    fn trailing_comma() {\n        assert_eq!(\"John,32,\".parse::<Person>(), Err(BadLen));\n    }\n\n    #[test]\n    fn trailing_comma_and_some_string() {\n        assert_eq!(\"John,32,man\".parse::<Person>(), Err(BadLen));\n    }\n}\n"
  },
  {
    "path": "exercises/23_conversions/try_from_into.rs",
    "content": "// `TryFrom` is a simple and safe type conversion that may fail in a controlled\n// way under some circumstances. Basically, this is the same as `From`. The main\n// difference is that this should return a `Result` type instead of the target\n// type itself. You can read more about it in the documentation:\n// https://doc.rust-lang.org/std/convert/trait.TryFrom.html\n\n#![allow(clippy::useless_vec)]\nuse std::convert::{TryFrom, TryInto};\n\n#[derive(Debug, PartialEq)]\nstruct Color {\n    red: u8,\n    green: u8,\n    blue: u8,\n}\n\n// We will use this error type for the `TryFrom` conversions.\n#[derive(Debug, PartialEq)]\nenum IntoColorError {\n    // Incorrect length of slice\n    BadLen,\n    // Integer conversion error\n    IntConversion,\n}\n\n// TODO: Tuple implementation.\n// Correct RGB color values must be integers in the 0..=255 range.\nimpl TryFrom<(i16, i16, i16)> for Color {\n    type Error = IntoColorError;\n\n    fn try_from(tuple: (i16, i16, i16)) -> Result<Self, Self::Error> {}\n}\n\n// TODO: Array implementation.\nimpl TryFrom<[i16; 3]> for Color {\n    type Error = IntoColorError;\n\n    fn try_from(arr: [i16; 3]) -> Result<Self, Self::Error> {}\n}\n\n// TODO: Slice implementation.\n// This implementation needs to check the slice length.\nimpl TryFrom<&[i16]> for Color {\n    type Error = IntoColorError;\n\n    fn try_from(slice: &[i16]) -> Result<Self, Self::Error> {}\n}\n\nfn main() {\n    // Using the `try_from` function.\n    let c1 = Color::try_from((183, 65, 14));\n    println!(\"{c1:?}\");\n\n    // Since `TryFrom` is implemented for `Color`, we can use `TryInto`.\n    let c2: Result<Color, _> = [183, 65, 14].try_into();\n    println!(\"{c2:?}\");\n\n    let v = vec![183, 65, 14];\n    // With slice we should use the `try_from` function\n    let c3 = Color::try_from(&v[..]);\n    println!(\"{c3:?}\");\n    // or put the slice within round brackets and use `try_into`.\n    let c4: Result<Color, _> = (&v[..]).try_into();\n    println!(\"{c4:?}\");\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n    use IntoColorError::*;\n\n    #[test]\n    fn test_tuple_out_of_range_positive() {\n        assert_eq!(Color::try_from((256, 1000, 10000)), Err(IntConversion));\n    }\n\n    #[test]\n    fn test_tuple_out_of_range_negative() {\n        assert_eq!(Color::try_from((-1, -10, -256)), Err(IntConversion));\n    }\n\n    #[test]\n    fn test_tuple_sum() {\n        assert_eq!(Color::try_from((-1, 255, 255)), Err(IntConversion));\n    }\n\n    #[test]\n    fn test_tuple_correct() {\n        let c: Result<Color, _> = (183, 65, 14).try_into();\n        assert!(c.is_ok());\n        assert_eq!(\n            c.unwrap(),\n            Color {\n                red: 183,\n                green: 65,\n                blue: 14,\n            }\n        );\n    }\n\n    #[test]\n    fn test_array_out_of_range_positive() {\n        let c: Result<Color, _> = [1000, 10000, 256].try_into();\n        assert_eq!(c, Err(IntConversion));\n    }\n\n    #[test]\n    fn test_array_out_of_range_negative() {\n        let c: Result<Color, _> = [-10, -256, -1].try_into();\n        assert_eq!(c, Err(IntConversion));\n    }\n\n    #[test]\n    fn test_array_sum() {\n        let c: Result<Color, _> = [-1, 255, 255].try_into();\n        assert_eq!(c, Err(IntConversion));\n    }\n\n    #[test]\n    fn test_array_correct() {\n        let c: Result<Color, _> = [183, 65, 14].try_into();\n        assert!(c.is_ok());\n        assert_eq!(\n            c.unwrap(),\n            Color {\n                red: 183,\n                green: 65,\n                blue: 14\n            }\n        );\n    }\n\n    #[test]\n    fn test_slice_out_of_range_positive() {\n        let arr = [10000, 256, 1000];\n        assert_eq!(Color::try_from(&arr[..]), Err(IntConversion));\n    }\n\n    #[test]\n    fn test_slice_out_of_range_negative() {\n        let arr = [-256, -1, -10];\n        assert_eq!(Color::try_from(&arr[..]), Err(IntConversion));\n    }\n\n    #[test]\n    fn test_slice_sum() {\n        let arr = [-1, 255, 255];\n        assert_eq!(Color::try_from(&arr[..]), Err(IntConversion));\n    }\n\n    #[test]\n    fn test_slice_correct() {\n        let v = vec![183, 65, 14];\n        let c: Result<Color, _> = Color::try_from(&v[..]);\n        assert!(c.is_ok());\n        assert_eq!(\n            c.unwrap(),\n            Color {\n                red: 183,\n                green: 65,\n                blue: 14,\n            }\n        );\n    }\n\n    #[test]\n    fn test_slice_excess_length() {\n        let v = vec![0, 0, 0, 0];\n        assert_eq!(Color::try_from(&v[..]), Err(BadLen));\n    }\n\n    #[test]\n    fn test_slice_insufficient_length() {\n        let v = vec![0, 0];\n        assert_eq!(Color::try_from(&v[..]), Err(BadLen));\n    }\n}\n"
  },
  {
    "path": "exercises/23_conversions/using_as.rs",
    "content": "// Type casting in Rust is done via the usage of the `as` operator.\n// Note that the `as` operator is not only used when type casting. It also helps\n// with renaming imports.\n\nfn average(values: &[f64]) -> f64 {\n    let total = values.iter().sum::<f64>();\n    // TODO: Make a conversion before dividing.\n    total / values.len()\n}\n\nfn main() {\n    let values = [3.5, 0.3, 13.0, 11.7];\n    println!(\"{}\", average(&values));\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    #[test]\n    fn returns_proper_type_and_value() {\n        assert_eq!(average(&[3.5, 0.3, 13.0, 11.7]), 7.125);\n    }\n}\n"
  },
  {
    "path": "exercises/README.md",
    "content": "# Exercise to Book Chapter mapping\n\n| Exercise               | Book Chapter        |\n| ---------------------- | ------------------- |\n| variables              | §3.1                |\n| functions              | §3.3                |\n| if                     | §3.5                |\n| primitive_types        | §3.2, §4.3          |\n| vecs                   | §8.1                |\n| move_semantics         | §4.1-2              |\n| structs                | §5.1, §5.3          |\n| enums                  | §6, §19.3           |\n| strings                | §8.2                |\n| modules                | §7                  |\n| hashmaps               | §8.3                |\n| options                | §10.1               |\n| error_handling         | §9                  |\n| generics               | §10                 |\n| traits                 | §10.2               |\n| lifetimes              | §10.3               |\n| tests                  | §11.1               |\n| iterators              | §13.2-4             |\n| smart_pointers         | §15, §16.3          |\n| threads                | §16.1-3             |\n| macros                 | §20.5               |\n| clippy                 | Appendix D          |\n| conversions            | n/a                 |\n"
  },
  {
    "path": "exercises/quizzes/README.md",
    "content": "# Quizzes\n\nAfter every couple of sections, there will be a quiz in this directory that'll test your knowledge on a bunch of sections at once.\n"
  },
  {
    "path": "exercises/quizzes/quiz1.rs",
    "content": "// This is a quiz for the following sections:\n// - Variables\n// - Functions\n// - If\n//\n// Mary is buying apples. The price of an apple is calculated as follows:\n// - An apple costs 2 rustbucks.\n// - However, if Mary buys more than 40 apples, the price of each apple in the\n// entire order is reduced to only 1 rustbuck!\n\n// TODO: Write a function that calculates the price of an order of apples given\n// the quantity bought.\n// fn calculate_price_of_apples(???) -> ??? { ??? }\n\nfn main() {\n    // You can optionally experiment here.\n}\n\n// Don't change the tests!\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    #[test]\n    fn verify_test() {\n        assert_eq!(calculate_price_of_apples(35), 70);\n        assert_eq!(calculate_price_of_apples(40), 80);\n        assert_eq!(calculate_price_of_apples(41), 41);\n        assert_eq!(calculate_price_of_apples(65), 65);\n    }\n}\n"
  },
  {
    "path": "exercises/quizzes/quiz2.rs",
    "content": "// This is a quiz for the following sections:\n// - Strings\n// - Vecs\n// - Move semantics\n// - Modules\n// - Enums\n//\n// Let's build a little machine in the form of a function. As input, we're going\n// to give a list of strings and commands. These commands determine what action\n// is going to be applied to the string. It can either be:\n// - Uppercase the string\n// - Trim the string\n// - Append \"bar\" to the string a specified amount of times\n//\n// The exact form of this will be:\n// - The input is going to be a Vector of 2-length tuples,\n//   the first element is the string, the second one is the command.\n// - The output element is going to be a vector of strings.\n\nenum Command {\n    Uppercase,\n    Trim,\n    Append(usize),\n}\n\nmod my_module {\n    use super::Command;\n\n    // TODO: Complete the function as described above.\n    // pub fn transformer(input: ???) -> ??? { ??? }\n}\n\nfn main() {\n    // You can optionally experiment here.\n}\n\n#[cfg(test)]\nmod tests {\n    // TODO: What do we need to import to have `transformer` in scope?\n    // use ???;\n    use super::Command;\n\n    #[test]\n    fn it_works() {\n        let input = vec![\n            (\"hello\".to_string(), Command::Uppercase),\n            (\" all roads lead to rome! \".to_string(), Command::Trim),\n            (\"foo\".to_string(), Command::Append(1)),\n            (\"bar\".to_string(), Command::Append(5)),\n        ];\n        let output = transformer(input);\n\n        assert_eq!(\n            output,\n            [\n                \"HELLO\",\n                \"all roads lead to rome!\",\n                \"foobar\",\n                \"barbarbarbarbarbar\",\n            ]\n        );\n    }\n}\n"
  },
  {
    "path": "exercises/quizzes/quiz3.rs",
    "content": "// This quiz tests:\n// - Generics\n// - Traits\n//\n// An imaginary magical school has a new report card generation system written\n// in Rust! Currently, the system only supports creating report cards where the\n// student's grade is represented numerically (e.g. 1.0 -> 5.5). However, the\n// school also issues alphabetical grades (A+ -> F-) and needs to be able to\n// print both types of report card!\n//\n// Make the necessary code changes in the struct `ReportCard` and the impl\n// block to support alphabetical report cards in addition to numerical ones.\n\n// TODO: Adjust the struct as described above.\nstruct ReportCard {\n    grade: f32,\n    student_name: String,\n    student_age: u8,\n}\n\n// TODO: Adjust the impl block as described above.\nimpl ReportCard {\n    fn print(&self) -> String {\n        format!(\n            \"{} ({}) - achieved a grade of {}\",\n            &self.student_name, &self.student_age, &self.grade,\n        )\n    }\n}\n\nfn main() {\n    // You can optionally experiment here.\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    #[test]\n    fn generate_numeric_report_card() {\n        let report_card = ReportCard {\n            grade: 2.1,\n            student_name: \"Tom Wriggle\".to_string(),\n            student_age: 12,\n        };\n        assert_eq!(\n            report_card.print(),\n            \"Tom Wriggle (12) - achieved a grade of 2.1\",\n        );\n    }\n\n    #[test]\n    fn generate_alphabetic_report_card() {\n        let report_card = ReportCard {\n            grade: \"A+\",\n            student_name: \"Gary Plotter\".to_string(),\n            student_age: 11,\n        };\n        assert_eq!(\n            report_card.print(),\n            \"Gary Plotter (11) - achieved a grade of A+\",\n        );\n    }\n}\n"
  },
  {
    "path": "release-hook.sh",
    "content": "#!/bin/bash\n\n# Error out if any command fails\nset -e\n\ntypos\n\n# Similar to CI\ncargo clippy -- --deny warnings\ncargo fmt --all --check\ncargo test --workspace\ncargo dev check --require-solutions\n\n# MSRV\ncargo +1.88 dev check --require-solutions\n"
  },
  {
    "path": "rustlings-macros/Cargo.toml",
    "content": "[package]\nname = \"rustlings-macros\"\ndescription = \"A macros crate intended to be used only by Rustlings\"\nversion.workspace = true\nauthors.workspace = true\nrepository.workspace = true\nlicense.workspace = true\nedition.workspace = true\nrust-version.workspace = true\ninclude = [\n  \"/src/\",\n  \"/info.toml\",\n]\n\n[lib]\nproc-macro = true\n\n[dependencies]\nquote = \"1\"\nserde.workspace = true\ntoml.workspace = true\n\n[lints]\nworkspace = true\n"
  },
  {
    "path": "rustlings-macros/info.toml",
    "content": "format_version = 1\n\nwelcome_message = \"\"\"\nIs this your first time? Don't worry, Rustlings is made for beginners!\nWe are going to teach you a lot of things about Rust, but before we can\nget started, here are some notes about how Rustlings operates:\n\n1. The central concept behind Rustlings is that you solve exercises. These\n   exercises usually contain some compiler or logic errors which cause the\n   exercise to fail compilation or testing. It's your job to find all errors\n   and fix them!\n2. Make sure to have your editor open in the `rustlings/` directory. Rustlings\n   will show you the path of the current exercise under the progress bar. Open\n   the exercise file in your editor, fix errors and save the file. Rustlings\n   will automatically detect the file change and rerun the exercise. If all\n   errors are fixed, Rustlings will ask you to move on to the next exercise.\n3. If you're stuck on an exercise, enter `h` to show a hint.\n4. If an exercise doesn't make sense to you, feel free to open an issue on\n   GitHub! (https://github.com/rust-lang/rustlings). We look at every issue, and\n   sometimes, other learners do too so you can help each other out!\"\"\"\n\nfinal_message = \"\"\"\nWe hope you enjoyed learning about the various aspects of Rust!\nIf you noticed any issues, don't hesitate to report them on GitHub.\nYou can also contribute your own exercises to help the greater community!\n\nBefore reporting an issue or contributing, please read our guidelines:\nhttps://github.com/rust-lang/rustlings/blob/main/CONTRIBUTING.md\"\"\"\n\n# INTRO\n\n[[exercises]]\nname = \"intro1\"\ndir = \"00_intro\"\ntest = false\nskip_check_unsolved = true\nhint = \"\"\"\nEnter `n` to move on to the next exercise.\nYou might need to press ENTER after typing `n`.\"\"\"\n\n[[exercises]]\nname = \"intro2\"\ndir = \"00_intro\"\ntest = false\nhint = \"\"\"\nThe compiler is informing us that we've got the name of the print macro wrong.\nIt also suggests an alternative.\"\"\"\n\n# VARIABLES\n\n[[exercises]]\nname = \"variables1\"\ndir = \"01_variables\"\ntest = false\nhint = \"\"\"\nThe declaration in the `main` function is missing a keyword that is needed\nin Rust to create a new variable binding.\"\"\"\n\n[[exercises]]\nname = \"variables2\"\ndir = \"01_variables\"\ntest = false\nhint = \"\"\"\nThe compiler message is saying that Rust can't infer the type that the\nvariable binding `x` has with what is given here.\n\nWhat happens if you annotate the first line in the `main` function with a type\nannotation?\n\nWhat if you give `x` a value?\n\nWhat if you do both?\n\nWhat type should `x` be, anyway?\n\nWhat if `x` is the same type as `10`? What if it's a different type?\"\"\"\n\n[[exercises]]\nname = \"variables3\"\ndir = \"01_variables\"\ntest = false\nhint = \"\"\"\nIn this exercise, we have a variable binding that we've created in the `main`\nfunction, and we're trying to use it in the next line, but we haven't given it\na value.\n\nWe can't print out something that isn't there; try giving `x` a value!\n\nThis is an error that can cause bugs that's very easy to make in any\nprogramming language -- thankfully the Rust compiler has caught this for us!\"\"\"\n\n[[exercises]]\nname = \"variables4\"\ndir = \"01_variables\"\ntest = false\nhint = \"\"\"\nIn Rust, variable bindings are immutable by default. But here, we're trying\nto reassign a different value to `x`! There's a keyword we can use to make\na variable binding mutable instead.\"\"\"\n\n[[exercises]]\nname = \"variables5\"\ndir = \"01_variables\"\ntest = false\nhint = \"\"\"\nIn `variables4` we already learned how to make an immutable variable mutable\nusing a special keyword. Unfortunately this doesn't help us much in this\nexercise because we want to assign a different typed value to an existing\nvariable. Sometimes you may also like to reuse existing variable names because\nyou are just converting values to different types like in this exercise.\n\nFortunately Rust has a powerful solution to this problem: 'Shadowing'!\nYou can read more about 'Shadowing' in the book's section 'Variables and\nMutability':\nhttps://doc.rust-lang.org/book/ch03-01-variables-and-mutability.html#shadowing\n\nTry to solve this exercise afterwards using this technique.\"\"\"\n\n[[exercises]]\nname = \"variables6\"\ndir = \"01_variables\"\ntest = false\nhint = \"\"\"\nWe know about variables and mutability, but there is another important type of\nvariable available: constants.\n\nConstants are always immutable. They are declared with the keyword `const`\ninstead of `let`.\n\nThe type of Constants must always be annotated.\n\nRead more about constants and the differences between variables and constants\nunder 'Constants' in the book's section 'Variables and Mutability':\nhttps://doc.rust-lang.org/book/ch03-01-variables-and-mutability.html#constants\"\"\"\n\n# FUNCTIONS\n\n[[exercises]]\nname = \"functions1\"\ndir = \"02_functions\"\ntest = false\nhint = \"\"\"\nThis `main` function is calling a function that it expects to exist, but the\nfunction doesn't exist. It expects this function to have the name `call_me`.\nIt also expects this function to not take any arguments and not return a value.\nSounds a lot like `main`, doesn't it?\"\"\"\n\n[[exercises]]\nname = \"functions2\"\ndir = \"02_functions\"\ntest = false\nhint = \"\"\"\nRust requires that all parts of a function's signature have type annotations,\nbut `call_me` is missing the type annotation of `num`.\"\"\"\n\n[[exercises]]\nname = \"functions3\"\ndir = \"02_functions\"\ntest = false\nhint = \"\"\"\nThis time, the function *declaration* is okay, but there's something wrong\nwith the place where we are calling the function.\"\"\"\n\n[[exercises]]\nname = \"functions4\"\ndir = \"02_functions\"\ntest = false\nhint = \"\"\"\nThe error message points to the function `sale_price` and says it expects a type\nafter `->`. This is where the function's return type should be.\nTake a look at the `is_even` function for an example!\"\"\"\n\n[[exercises]]\nname = \"functions5\"\ndir = \"02_functions\"\ntest = false\nhint = \"\"\"\nThis is a really common error that can be fixed by removing one character.\nIt happens because Rust distinguishes between expressions and statements:\nExpressions return a value based on their operand(s), and statements simply\nreturn a `()` type which behaves just like `void` in C/C++.\n\nWe want to return a value with the type `i32` from the `square` function, but\nit is returning the type `()`.\n\nThere are two solutions:\n1. Add the `return` keyword before `num * num;`\n2. Remove the semicolon `;` after `num * num`\"\"\"\n\n# IF\n\n[[exercises]]\nname = \"if1\"\ndir = \"03_if\"\nhint = \"\"\"\nIt's possible to do this in one line if you would like!\n\nSome similar examples from other languages:\n- In C(++) this would be: `a > b ? a : b`\n- In Python this would be:  `a if a > b else b`\n\nRemember in Rust that:\n- The `if` condition does not need to be surrounded by parentheses\n- `if`/`else` conditionals are expressions\n- Each condition is followed by a `{}` block\"\"\"\n\n[[exercises]]\nname = \"if2\"\ndir = \"03_if\"\nhint = \"\"\"\nFor that first compiler error, it's important in Rust that each conditional\nblock returns the same type!\n\nTo get the tests passing, you will need a couple conditions checking different\ninput values. Read the tests to find out what they expect.\"\"\"\n\n[[exercises]]\nname = \"if3\"\ndir = \"03_if\"\nhint = \"\"\"\nIn Rust, every arm of an `if` expression has to return the same type of value.\nMake sure the type is consistent across all arms.\"\"\"\n\n# QUIZ 1\n\n[[exercises]]\nname = \"quiz1\"\ndir = \"quizzes\"\nhint = \"No hints this time ;)\"\n\n# PRIMITIVE TYPES\n\n[[exercises]]\nname = \"primitive_types1\"\ndir = \"04_primitive_types\"\ntest = false\nhint = \"\"\"\nIn Rust, a boolean can be negated using the operator `!` before it.\nExample: `!true == false`\nThis also works with boolean variables.\"\"\"\n\n[[exercises]]\nname = \"primitive_types2\"\ndir = \"04_primitive_types\"\ntest = false\nhint = \"No hints this time ;)\"\n\n[[exercises]]\nname = \"primitive_types3\"\ndir = \"04_primitive_types\"\ntest = false\nhint = \"\"\"\nThere's a shorthand to initialize arrays with a certain size that doesn't\nrequire you to type in 100 items (but you certainly can if you want!).\n\nFor example, you can do:\n```\nlet array = [\"Are we there yet?\"; 100];\n```\n\nBonus: what are some other things you could have that would return `true`\nfor `a.len() >= 100`?\"\"\"\n\n[[exercises]]\nname = \"primitive_types4\"\ndir = \"04_primitive_types\"\nhint = \"\"\"\nTake a look at the 'Understanding Ownership -> Slices -> Other Slices' section\nof the book: https://doc.rust-lang.org/book/ch04-03-slices.html and use the\nstarting and ending (plus one) indices of the items in the array that you want\nto end up in the slice.\n\nIf you're curious why the first argument of `assert_eq!` does not have an\nampersand for a reference since the second argument is a reference, take a look\nat the coercion chapter of the nomicon:\nhttps://doc.rust-lang.org/nomicon/coercions.html\"\"\"\n\n[[exercises]]\nname = \"primitive_types5\"\ndir = \"04_primitive_types\"\ntest = false\nhint = \"\"\"\nTake a look at the 'Data Types -> The Tuple Type' section of the book:\nhttps://doc.rust-lang.org/book/ch03-02-data-types.html#the-tuple-type\nParticularly the part about destructuring (second to last example in the\nsection).\n\nYou'll need to make a pattern to bind `name` and `age` to the appropriate parts\nof the tuple.\"\"\"\n\n[[exercises]]\nname = \"primitive_types6\"\ndir = \"04_primitive_types\"\nhint = \"\"\"\nWhile you could use a destructuring `let` for the tuple here, try\nindexing into it instead, as explained in the last example of the\n'Data Types -> The Tuple Type' section of the book:\nhttps://doc.rust-lang.org/book/ch03-02-data-types.html#the-tuple-type\nNow, you have another tool in your toolbox!\"\"\"\n\n# VECS\n\n[[exercises]]\nname = \"vecs1\"\ndir = \"05_vecs\"\nhint = \"\"\"\nIn Rust, there are two ways to define a Vector.\n1. One way is to use the `Vec::new()` function to create a new vector\n   and fill it with the `push()` method.\n2. The second way is to use the `vec![]` macro and define your elements\n   inside the square brackets. This way is simpler when you exactly know\n   the initial values.\n\nCheck this chapter: https://doc.rust-lang.org/book/ch08-01-vectors.html\nof the Rust book to learn more.\"\"\"\n\n[[exercises]]\nname = \"vecs2\"\ndir = \"05_vecs\"\nhint = \"\"\"\nUse the `.push()` method on the vector to push new elements to it.\"\"\"\n\n# MOVE SEMANTICS\n\n[[exercises]]\nname = \"move_semantics1\"\ndir = \"06_move_semantics\"\nhint = \"\"\"\nSo you've got the \"cannot borrow `vec` as mutable, as it is not declared as\nmutable\" error on the line where we push an element to the vector, right?\n\nThe fix for this is going to be adding one keyword, and the addition is NOT on\nthe line where we push to the vector (where the error is).\n\nTry accessing `vec0` after having called `fill_vec()`. See what happens!\"\"\"\n\n[[exercises]]\nname = \"move_semantics2\"\ndir = \"06_move_semantics\"\nhint = \"\"\"\nWhen running this exercise for the first time, you'll notice an error about\n\"borrow of moved value\". In Rust, when an argument is passed to a function and\nit's not explicitly returned, you can't use the original variable anymore.\nWe call this \"moving\" a variable. When we pass `vec0` into `fill_vec`, it's\nbeing \"moved\" into `vec1`, meaning we can't access `vec0` anymore.\n\nYou could make another, separate version of the data that's in `vec0` and\npass it to `fill_vec` instead. This is called cloning in Rust.\"\"\"\n\n[[exercises]]\nname = \"move_semantics3\"\ndir = \"06_move_semantics\"\nhint = \"\"\"\nThe difference between this one and the previous ones is that the first line\nof `fn fill_vec` that had `let mut vec = vec;` is no longer there. You can,\ninstead of adding that line back, add `mut` in one place that will change\nan existing binding to be a mutable binding instead of an immutable one :)\"\"\"\n\n[[exercises]]\nname = \"move_semantics4\"\ndir = \"06_move_semantics\"\nhint = \"\"\"\nCarefully reason about the range in which each mutable reference is in\nscope. Does it help to update the value of `x` immediately after\nthe mutable reference is taken?\nRead more about 'Mutable References' in the book's section 'References and\nBorrowing':\nhttps://doc.rust-lang.org/book/ch04-02-references-and-borrowing.html#mutable-references.\"\"\"\n\n[[exercises]]\nname = \"move_semantics5\"\ndir = \"06_move_semantics\"\ntest = false\nhint = \"\"\"\nTo find the answer, you can consult the book section \"References and Borrowing\":\nhttps://doc.rust-lang.org/book/ch04-02-references-and-borrowing.html\n\nThe first problem is that `get_char` is taking ownership of the string. So\n`data` is moved and can't be used for `string_uppercase`. `data` is moved to\n`get_char` first, meaning that `string_uppercase` can't manipulate the data.\n\nOnce you've fixed that, `string_uppercase`'s function signature will also need\nto be adjusted.\"\"\"\n\n# STRUCTS\n\n[[exercises]]\nname = \"structs1\"\ndir = \"07_structs\"\nhint = \"\"\"\nRust has more than one type of struct. Three actually, all variants are used to\npackage related data together.\n\nThere are regular structs. These are named collections of related data stored in\nfields.\n\nTuple structs are basically just named tuples.\n\nFinally, unit structs. These don't have any fields and are useful for generics.\n\nIn this exercise, you need to complete and implement one of each kind.\nRead more about structs in The Book:\nhttps://doc.rust-lang.org/book/ch05-01-defining-structs.html\"\"\"\n\n[[exercises]]\nname = \"structs2\"\ndir = \"07_structs\"\nhint = \"\"\"\nCreating instances of structs is easy, all you need to do is assign some values\nto its fields.\n\nThere are however some shortcuts that can be taken when instantiating structs.\nHave a look in The Book to find out more:\nhttps://doc.rust-lang.org/book/ch05-01-defining-structs.html#creating-instances-from-other-instances-with-struct-update-syntax\"\"\"\n\n[[exercises]]\nname = \"structs3\"\ndir = \"07_structs\"\nhint = \"\"\"\nFor `is_international`: What makes a package international? Seems related to\nthe places it goes through right?\n\nFor `get_fees`: This method takes an additional argument, is there a field in\nthe `Package` struct that this relates to?\n\nHave a look in The Book to find out more about method implementations:\nhttps://doc.rust-lang.org/book/ch05-03-method-syntax.html\"\"\"\n\n# ENUMS\n\n[[exercises]]\nname = \"enums1\"\ndir = \"08_enums\"\ntest = false\nhint = \"No hints this time ;)\"\n\n[[exercises]]\nname = \"enums2\"\ndir = \"08_enums\"\ntest = false\nhint = \"\"\"\nYou can create enumerations that have different variants with different types\nsuch as anonymous structs, structs, a single string, tuples, no data, etc.\"\"\"\n\n[[exercises]]\nname = \"enums3\"\ndir = \"08_enums\"\nhint = \"\"\"\nAs a first step, define enums to compile the code without errors.\n\nThen, create a match expression in `process()`.\n\nNote that you need to deconstruct some message variants in the match expression\nto get the variant's values.\"\"\"\n\n# STRINGS\n\n[[exercises]]\nname = \"strings1\"\ndir = \"09_strings\"\ntest = false\nhint = \"\"\"\nThe `current_favorite_color` function is currently returning a string slice\nwith the `'static` lifetime. We know this because the data of the string lives\nin our code itself -- it doesn't come from a file or user input or another\nprogram -- so it will live as long as our program lives.\n\nBut it is still a string slice. There's one way to create a `String` by\nconverting a string slice covered in the Strings chapter of the book, and\nanother way that uses the `From` trait.\"\"\"\n\n[[exercises]]\nname = \"strings2\"\ndir = \"09_strings\"\ntest = false\nhint = \"\"\"\nYes, it would be really easy to fix this by just changing the value bound to\n`word` to be a string slice instead of a `String`, wouldn't it? There is a way\nto add one character to the `if` statement, though, that will coerce the\n`String` into a string slice.\n\nSide note: If you're interested in learning about how this kind of reference\nconversion works, you can jump ahead in the book and read this part in the\nsmart pointers chapter:\nhttps://doc.rust-lang.org/book/ch15-02-deref.html#implicit-deref-coercions-with-functions-and-methods\"\"\"\n\n[[exercises]]\nname = \"strings3\"\ndir = \"09_strings\"\nhint = \"\"\"\nThere are many useful standard library functions for strings. Let's try and use\nsome of them:\nhttps://doc.rust-lang.org/std/string/struct.String.html#method.trim\n\nFor the `compose_me` method: You can either use the `format!` macro, or convert\nthe string slice into an owned string, which you can then freely extend.\n\nFor the `replace_me` method, you can check out the `replace` method:\nhttps://doc.rust-lang.org/std/string/struct.String.html#method.replace\"\"\"\n\n[[exercises]]\nname = \"strings4\"\ndir = \"09_strings\"\ntest = false\nhint = \"\"\"\nReplace `placeholder` with either `string` or `string_slice` in the `main`\nfunction.\n\nExample:\n`placeholder(\"blue\");`\nshould become\n`string_slice(\"blue\");`\nbecause \"blue\" is `&str`, not `String`.\"\"\"\n\n# MODULES\n\n[[exercises]]\nname = \"modules1\"\ndir = \"10_modules\"\ntest = false\nhint = \"\"\"\nEverything is private in Rust by default. But there's a keyword we can use\nto make something public!\"\"\"\n\n[[exercises]]\nname = \"modules2\"\ndir = \"10_modules\"\ntest = false\nhint = \"\"\"\nThe `delicious_snacks` module is trying to present an external interface that\nis different than its internal structure (the `fruits` and `veggies` modules\nand associated constants). Complete the `use` statements to fit the uses in\n`main` and find the one keyword missing for both constants.\n\nLearn more in The Book:\nhttps://doc.rust-lang.org/book/ch07-04-bringing-paths-into-scope-with-the-use-keyword.html#re-exporting-names-with-pub-use\"\"\"\n\n[[exercises]]\nname = \"modules3\"\ndir = \"10_modules\"\ntest = false\nhint = \"\"\"\n`UNIX_EPOCH` and `SystemTime` are declared in the `std::time` module. Add a\n`use` statement for these two to bring them into scope. You can use nested\npaths to bring these two in using only one line.\"\"\"\n\n# HASHMAPS\n\n[[exercises]]\nname = \"hashmaps1\"\ndir = \"11_hashmaps\"\nhint = \"\"\"\nThe number of fruits should be at least 5 and you have to put at least 3\ndifferent types of fruits.\"\"\"\n\n[[exercises]]\nname = \"hashmaps2\"\ndir = \"11_hashmaps\"\nhint = \"\"\"\nUse the `entry()` and `or_insert()` methods of `HashMap` to achieve this.\n\nLearn more in The Book:\nhttps://doc.rust-lang.org/book/ch08-03-hash-maps.html#only-inserting-a-value-if-the-key-has-no-value\"\"\"\n\n[[exercises]]\nname = \"hashmaps3\"\ndir = \"11_hashmaps\"\nhint = \"\"\"\nHint 1: Use the `entry()` and `or_default()` methods of `HashMap` to insert the\n        default value of `TeamScores` if a team doesn't exist in the table yet.\n\nHint 2: If there is already an entry for a given key, the value returned by\n        `entry()` can be updated based on the existing value.\n\nLearn more in The Book:\nhttps://doc.rust-lang.org/book/ch08-03-hash-maps.html#updating-a-value-based-on-the-old-value\"\"\"\n\n# QUIZ 2\n\n[[exercises]]\nname = \"quiz2\"\ndir = \"quizzes\"\nhint = \"The `+` operator can concatenate a `String` with a `&str`.\"\n\n# OPTIONS\n\n[[exercises]]\nname = \"options1\"\ndir = \"12_options\"\nhint = \"\"\"\nOptions can have a `Some` value, with an inner value, or a `None` value,\nwithout an inner value.\n\nThere are multiple ways to get at the inner value, you can use `unwrap`, or\npattern match. Unwrapping is the easiest, but how do you do it safely so that\nit doesn't panic in your face later?\"\"\"\n\n[[exercises]]\nname = \"options2\"\ndir = \"12_options\"\nhint = \"\"\"\nCheck out:\n\n- https://doc.rust-lang.org/rust-by-example/flow_control/if_let.html\n- https://doc.rust-lang.org/rust-by-example/flow_control/while_let.html\n\nRemember that `Option`s can be nested in if-let and while-let statements.\n\nFor example: `if let Some(Some(x)) = y`\n\nAlso see `Option::flatten`\"\"\"\n\n[[exercises]]\nname = \"options3\"\ndir = \"12_options\"\ntest = false\nhint = \"\"\"\nThe compiler says a partial move happened in the `match` statement. How can\nthis be avoided? The compiler shows the correction needed.\n\nAfter making the correction as suggested by the compiler, read the related docs\npage:\nhttps://doc.rust-lang.org/std/keyword.ref.html\"\"\"\n\n# ERROR HANDLING\n\n[[exercises]]\nname = \"errors1\"\ndir = \"13_error_handling\"\nhint = \"\"\"\n`Ok` and `Err` are the two variants of `Result`, so what the tests are saying\nis that `generate_nametag_text` should return a `Result` instead of an `Option`.\n\nTo make this change, you'll need to:\n  - update the return type in the function signature to be a `Result<String,\n    String>` that could be the variants `Ok(String)` and `Err(String)`\n  - change the body of the function to return `Ok(…)` where it currently\n    returns `Some(…)`\n  - change the body of the function to return `Err(error message)` where it\n    currently returns `None`\"\"\"\n\n[[exercises]]\nname = \"errors2\"\ndir = \"13_error_handling\"\nhint = \"\"\"\nOne way to handle this is using a `match` statement on\n`item_quantity.parse::<i32>()` where the cases are `Ok(something)` and\n`Err(something)`.\n\nThis pattern is very common in Rust, though, so there's the `?` operator that\ndoes pretty much what you would make that match statement do for you!\n\nTake a look at this section of the \"Error Handling\" chapter:\nhttps://doc.rust-lang.org/book/ch09-02-recoverable-errors-with-result.html#a-shortcut-for-propagating-errors-the--operator\"\"\"\n\n[[exercises]]\nname = \"errors3\"\ndir = \"13_error_handling\"\ntest = false\nhint = \"\"\"\nIf other functions can return a `Result`, why shouldn't `main`? It's a fairly\ncommon convention to return something like `Result<(), ErrorType>` from your\n`main` function.\n\nThe unit type `()` is there because nothing is really needed in terms of a\npositive result.\"\"\"\n\n[[exercises]]\nname = \"errors4\"\ndir = \"13_error_handling\"\nhint = \"\"\"\n`PositiveNonzeroInteger::new` is always creating a new instance and returning\nan `Ok` result. But it should be doing some checking, returning an `Err` if\nthose checks fail, and only returning an `Ok` if those checks determine that\neverything is… okay :)\"\"\"\n\n[[exercises]]\nname = \"errors5\"\ndir = \"13_error_handling\"\ntest = false\nhint = \"\"\"\nThere are two different possible `Result` types produced within the `main`\nfunction, which are propagated using the `?` operators. How do we declare a\nreturn type for the `main` function that allows both?\n\nUnder the hood, the `?` operator calls `From::from` on the error value to\nconvert it to a boxed trait object, a `Box<dyn Error>`. This boxed trait object\nis polymorphic, and since all errors implement the `Error` trait, we can capture\nlots of different errors in one `Box` object.\n\nCheck out this section of The Book:\nhttps://doc.rust-lang.org/book/ch09-02-recoverable-errors-with-result.html#a-shortcut-for-propagating-errors-the--operator\n\nRead more about boxing errors:\nhttps://doc.rust-lang.org/stable/rust-by-example/error/multiple_error_types/boxing_errors.html\n\nRead more about using the `?` operator with boxed errors:\nhttps://doc.rust-lang.org/stable/rust-by-example/error/multiple_error_types/reenter_question_mark.html\"\"\"\n\n[[exercises]]\nname = \"errors6\"\ndir = \"13_error_handling\"\nhint = \"\"\"\nThis exercise uses a completed version of `PositiveNonzeroInteger` from the\nprevious exercises.\n\nBelow the line that `TODO` asks you to change, there is an example of using\nthe `map_err()` method on a `Result` to transform one type of error into\nanother. Try using something similar on the `Result` from `parse()`. You\ncan then use the `?` operator to return early.\n\nRead more about `map_err()` in the `std::result` documentation:\nhttps://doc.rust-lang.org/std/result/enum.Result.html#method.map_err\"\"\"\n\n# Generics\n\n[[exercises]]\nname = \"generics1\"\ndir = \"14_generics\"\ntest = false\nhint = \"\"\"\nVectors in Rust make use of generics to create dynamically sized arrays of any\ntype.\nIf the vector `numbers` has the type `Vec<T>`, then we can only push values of\ntype `T` to it. By using `into()` before pushing, we ask the compiler to convert\n`n1` and `n2` to `T`. But the compiler doesn't know what `T` is yet and needs a\ntype annotation.\n\n`u8` and `i8` can both be converted to `i16`, `i32` and `i64`. Choose one for\nthe generic of the vector.\"\"\"\n\n[[exercises]]\nname = \"generics2\"\ndir = \"14_generics\"\nhint = \"\"\"\nRelated section in The Book:\nhttps://doc.rust-lang.org/book/ch10-01-syntax.html#in-method-definitions\"\"\"\n\n# TRAITS\n\n[[exercises]]\nname = \"traits1\"\ndir = \"15_traits\"\nhint = \"\"\"\nMore about traits in The Book:\nhttps://doc.rust-lang.org/book/ch10-02-traits.html\n\nThe `+` operator can concatenate a `String` with a `&str`.\"\"\"\n\n[[exercises]]\nname = \"traits2\"\ndir = \"15_traits\"\nhint = \"\"\"\nNotice how the trait takes ownership of `self` and returns `Self`.\n\nAlthough the signature of `append_bar` in the trait takes `self` as argument,\nthe implementation can take `mut self` instead. This is possible because the\nvalue is owned anyway.\"\"\"\n\n[[exercises]]\nname = \"traits3\"\ndir = \"15_traits\"\nhint = \"\"\"\nTraits can have a default implementation for functions. Data types that\nimplement the trait can then use the default version of these functions\nif they choose not to implement the function themselves.\n\nRelated section in The Book:\nhttps://doc.rust-lang.org/book/ch10-02-traits.html#default-implementations\"\"\"\n\n[[exercises]]\nname = \"traits4\"\ndir = \"15_traits\"\nhint = \"\"\"\nInstead of using concrete types as parameters you can use traits. Try replacing\n`???` with `impl [what goes here?]`.\n\nRelated section in The Book:\nhttps://doc.rust-lang.org/book/ch10-02-traits.html#traits-as-parameters\"\"\"\n\n[[exercises]]\nname = \"traits5\"\ndir = \"15_traits\"\nhint = \"\"\"\nTo ensure a parameter implements multiple traits use the '+ syntax'. Try\nreplacing `???` with 'impl [what goes here?] + [what goes here?]'.\n\nRelated section in The Book:\nhttps://doc.rust-lang.org/book/ch10-02-traits.html#specifying-multiple-trait-bounds-with-the--syntax\"\"\"\n\n# QUIZ 3\n\n[[exercises]]\nname = \"quiz3\"\ndir = \"quizzes\"\nhint = \"\"\"\nTo find the best solution to this challenge, you need to recall your knowledge\nof traits, specifically \"Trait Bound Syntax\":\nhttps://doc.rust-lang.org/book/ch10-02-traits.html#trait-bound-syntax\n\nHere is how to specify a trait bound for an implementation block:\n`impl<T: Trait1 + Trait2 + …> for Foo<T> { … }`\n\nYou may need this:\n`use std::fmt::Display;`\"\"\"\n\n# LIFETIMES\n\n[[exercises]]\nname = \"lifetimes1\"\ndir = \"16_lifetimes\"\nhint = \"\"\"\nLet the compiler guide you. Also take a look at The Book if you need help:\nhttps://doc.rust-lang.org/book/ch10-03-lifetime-syntax.html\"\"\"\n\n[[exercises]]\nname = \"lifetimes2\"\ndir = \"16_lifetimes\"\ntest = false\nhint = \"\"\"\nRemember that the generic lifetime `'a` will get the concrete lifetime that is\nequal to the smaller of the lifetimes of `x` and `y`.\n\nYou can take at least two paths to achieve the desired result while keeping the\ninner block:\n1. Move the `string2` declaration to make it live as long as `string1` (how is\n   `result` declared?)\n2. Move `println!` into the inner block\"\"\"\n\n[[exercises]]\nname = \"lifetimes3\"\ndir = \"16_lifetimes\"\ntest = false\nhint = \"\"\"Let the compiler guide you :)\"\"\"\n\n# TESTS\n\n[[exercises]]\nname = \"tests1\"\ndir = \"17_tests\"\nhint = \"\"\"\n`assert!` is a macro that needs an argument. Depending on the value of the\nargument, `assert!` will do nothing (in which case the test will pass) or\n`assert!` will panic (in which case the test will fail).\n\nSo try giving different values to `assert!` and see which ones compile, which\nones pass, and which ones fail :)\n\nIf you want to check for `false`, you can negate the result of what you're\nchecking using `!`, like `assert!(!…)`.\"\"\"\n\n[[exercises]]\nname = \"tests2\"\ndir = \"17_tests\"\nhint = \"\"\"\n`assert_eq!` is a macro that takes two arguments and compares them. Try giving\nit two values that are equal! Try giving it two arguments that are different!\nTry switching which argument comes first and which comes second!\"\"\"\n\n[[exercises]]\nname = \"tests3\"\ndir = \"17_tests\"\nhint = \"\"\"\nWe expect the method `Rectangle::new` to panic for negative values.\n\nTo handle that, you need to add a special attribute to the test function.\n\nYou can refer to the docs:\nhttps://doc.rust-lang.org/book/ch11-01-writing-tests.html#checking-for-panics-with-should_panic\"\"\"\n\n# STANDARD LIBRARY TYPES\n\n[[exercises]]\nname = \"iterators1\"\ndir = \"18_iterators\"\nhint = \"\"\"\nAn iterator goes through all elements in a collection, but what if we've run\nout of elements? What should we expect here? If you're stuck, take a look at\nhttps://doc.rust-lang.org/std/iter/trait.Iterator.html\"\"\"\n\n[[exercises]]\nname = \"iterators2\"\ndir = \"18_iterators\"\nhint = \"\"\"\n`capitalize_first`:\n\nThe variable `first` is a `char`. It needs to be capitalized and added to the\nremaining characters in `chars` in order to return the correct `String`.\n\nThe remaining characters in `chars` can be viewed as a string slice using the\n`as_str` method.\n\nThe documentation for `char` contains many useful methods.\nhttps://doc.rust-lang.org/std/primitive.char.html\n\nUse `char::to_uppercase`. It returns an iterator that can be converted to a\n`String`.\n\n`capitalize_words_vector`:\n\nCreate an iterator from the slice. Transform the iterated values by applying\nthe `capitalize_first` function. Remember to `collect` the iterator.\n\n`capitalize_words_string`:\n\nThis is surprisingly similar to the previous solution. `collect` is very\npowerful and very general. Rust just needs to know the desired type.\"\"\"\n\n[[exercises]]\nname = \"iterators3\"\ndir = \"18_iterators\"\nhint = \"\"\"\nThe `divide` function needs to return the correct error when the divisor is 0 or\nwhen even division is not possible.\n\nThe `division_results` variable needs to be collected into a collection type.\n\nThe `result_with_list` function needs to return a single `Result` where the\nsuccess case is a vector of integers and the failure case is a `DivisionError`.\n\nThe `list_of_results` function needs to return a vector of results.\n\nSee https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.collect for\nhow the `FromIterator` trait is used in `collect()`. This trait is REALLY\npowerful! It can make the solution to this exercise much easier.\"\"\"\n\n[[exercises]]\nname = \"iterators4\"\ndir = \"18_iterators\"\nhint = \"\"\"\nIn an imperative language, you might write a `for` loop that updates a mutable\nvariable. Or, you might write code utilizing recursion and a match clause. In\nRust, you can take another functional approach, computing the factorial\nelegantly with ranges and iterators.\n\nCheck out the `fold` and `rfold` methods!\"\"\"\n\n[[exercises]]\nname = \"iterators5\"\ndir = \"18_iterators\"\nhint = \"\"\"\nThe documentation for the `std::iter::Iterator` trait contains numerous methods\nthat would be helpful here.\n\nThe `collection` variable in `count_collection_iterator` is a slice of\n`HashMap`s. It needs to be converted into an iterator in order to use the\niterator methods.\n\nThe `fold` method can be useful in the `count_collection_iterator` function.\n\nFor a further challenge, consult the documentation for `Iterator` to find\na different method that could make your code more compact than using `fold`.\"\"\"\n\n# SMART POINTERS\n\n[[exercises]]\nname = \"box1\"\ndir = \"19_smart_pointers\"\nhint = \"\"\"\nThe compiler's message should help: Since we cannot store the value of the\nactual type when working with recursive types, we need to store a reference\n(pointer) to its value.\n\nWe should, therefore, place our `List` inside a `Box`. More details in The Book:\nhttps://doc.rust-lang.org/book/ch15-01-box.html#enabling-recursive-types-with-boxes\n\nCreating an empty list should be fairly straightforward (Hint: Read the tests).\n\nFor a non-empty list, keep in mind that we want to use our `Cons` list builder.\nAlthough the current list is one of integers (`i32`), feel free to change the\ndefinition and try other types!\"\"\"\n\n[[exercises]]\nname = \"rc1\"\ndir = \"19_smart_pointers\"\nhint = \"\"\"\nThis is a straightforward exercise to use the `Rc<T>` type. Each `Planet` has\nownership of the `Sun`, and uses `Rc::clone()` to increment the reference count\nof the `Sun`.\n\nAfter using `drop()` to move the `Planet`s out of scope individually, the\nreference count goes down.\n\nIn the end, the `Sun` only has one reference again, to itself.\n\nSee more at: https://doc.rust-lang.org/book/ch15-04-rc.html\n\nUnfortunately, Pluto is no longer considered a planet :(\"\"\"\n\n[[exercises]]\nname = \"arc1\"\ndir = \"19_smart_pointers\"\ntest = false\nhint = \"\"\"\nMake `shared_numbers` be an `Arc` from the `numbers` vector. Then, in order\nto avoid creating a copy of `numbers`, you'll need to create `child_numbers`\ninside the loop but still in the main thread.\n\n`child_numbers` should be a clone of the `Arc` of the numbers instead of a\nthread-local copy of the numbers.\n\nThis is a simple exercise if you understand the underlying concepts, but if this\nis too much of a struggle, consider reading through all of Chapter 16 in The\nBook:\nhttps://doc.rust-lang.org/book/ch16-00-concurrency.html\"\"\"\n\n[[exercises]]\nname = \"cow1\"\ndir = \"19_smart_pointers\"\nhint = \"\"\"\nIf `Cow` already owns the data, it doesn't need to clone it when `to_mut()` is\ncalled.\n\nCheck out the documentation of the `Cow` type:\nhttps://doc.rust-lang.org/std/borrow/enum.Cow.html\"\"\"\n\n# THREADS\n\n[[exercises]]\nname = \"threads1\"\ndir = \"20_threads\"\ntest = false\nhint = \"\"\"\n`JoinHandle` is a struct that is returned from a spawned thread:\nhttps://doc.rust-lang.org/std/thread/fn.spawn.html\n\nA challenge with multi-threaded applications is that the main thread can\nfinish before the spawned threads are done.\nhttps://doc.rust-lang.org/book/ch16-01-threads.html#waiting-for-all-threads-to-finish-using-join-handles\n\nUse the `JoinHandle`s to wait for each thread to finish and collect their\nresults.\n\nhttps://doc.rust-lang.org/std/thread/struct.JoinHandle.html\"\"\"\n\n[[exercises]]\nname = \"threads2\"\ndir = \"20_threads\"\ntest = false\nhint = \"\"\"\n`Arc` is an Atomic Reference Counted pointer that allows safe, shared access\nto **immutable** data. But we want to *change* the number of `jobs_done` so\nwe'll need to also use another type that will only allow one thread to mutate\nthe data at a time. Take a look at this section of the book:\nhttps://doc.rust-lang.org/book/ch16-03-shared-state.html#atomic-reference-counting-with-arct\n\nKeep reading if you'd like more hints :)\n\nDo you now have an `Arc<Mutex<JobStatus>>` at the beginning of `main`? Like:\n```\nlet status = Arc::new(Mutex::new(JobStatus { jobs_done: 0 }));\n```\n\nSimilar to the code in the following example in The Book:\nhttps://doc.rust-lang.org/book/ch16-03-shared-state.html#sharing-a-mutext-between-multiple-threads\"\"\"\n\n[[exercises]]\nname = \"threads3\"\ndir = \"20_threads\"\nhint = \"\"\"\nAn alternate way to handle concurrency between threads is to use an `mpsc`\n(multiple producer, single consumer) channel to communicate.\n\nWith both a sending end and a receiving end, it's possible to send values in\none thread and receive them in another.\n\nMultiple producers are possible by using `clone()` to create a duplicate of the\noriginal sending end.\n\nRelated section in The Book:\nhttps://doc.rust-lang.org/book/ch16-02-message-passing.html\"\"\"\n\n# MACROS\n\n[[exercises]]\nname = \"macros1\"\ndir = \"21_macros\"\ntest = false\nhint = \"\"\"\nWhen you call a macro, you need to add something special compared to a regular\nfunction call.\"\"\"\n\n[[exercises]]\nname = \"macros2\"\ndir = \"21_macros\"\ntest = false\nhint = \"\"\"\nMacros don't quite play by the same rules as the rest of Rust, in terms of\nwhat's available where.\n\nUnlike other things in Rust, the order of \"where you define a macro\" versus\n\"where you use it\" actually matters.\"\"\"\n\n[[exercises]]\nname = \"macros3\"\ndir = \"21_macros\"\ntest = false\nhint = \"\"\"\nIn order to use a macro outside of its module, you need to do something\nspecial to the module to lift the macro out into its parent.\"\"\"\n\n[[exercises]]\nname = \"macros4\"\ndir = \"21_macros\"\ntest = false\nhint = \"\"\"\nYou only need to add a single character to make this compile.\n\nThe way macros are written, it wants to see something between each \"macro arm\",\nso it can separate them.\n\nThat's all the macro exercises we have in here, but it's barely even scratching\nthe surface of what you can do with Rust's macros. For a more thorough\nintroduction, you can have a read through 'The Little Book of Rust Macros':\nhttps://veykril.github.io/tlborm/\"\"\"\n\n#  CLIPPY\n\n[[exercises]]\nname = \"clippy1\"\ndir = \"22_clippy\"\ntest = false\nstrict_clippy = true\nhint = \"\"\"\nRust stores the highest precision version of some long or infinite precision\nmathematical constants in the Rust standard library:\nhttps://doc.rust-lang.org/stable/std/f32/consts/index.html\n\nWe may be tempted to use our own approximations for certain mathematical\nconstants, but clippy recognizes those imprecise mathematical constants as a\nsource of potential error.\n\nSee the suggestions of the Clippy warning in the compile output and use the\nappropriate replacement constant from `std::f32::consts`.\"\"\"\n\n[[exercises]]\nname = \"clippy2\"\ndir = \"22_clippy\"\ntest = false\nstrict_clippy = true\nhint = \"\"\"\n`for` loops over `Option` values are more clearly expressed as an `if-let`\nstatement.\n\nNot required to solve this exercise, but if you are interested in when iterating\nover `Option` can be useful, read the following section in the documentation:\nhttps://doc.rust-lang.org/std/option/#iterating-over-option\"\"\"\n\n[[exercises]]\nname = \"clippy3\"\ndir = \"22_clippy\"\ntest = false\nstrict_clippy = true\nhint = \"No hints this time!\"\n\n# TYPE CONVERSIONS\n\n[[exercises]]\nname = \"using_as\"\ndir = \"23_conversions\"\nhint = \"\"\"\nUse the `as` operator to cast one of the operands in the last line of the\n`average` function into the expected return type.\"\"\"\n\n[[exercises]]\nname = \"from_into\"\ndir = \"23_conversions\"\nhint = \"\"\"\nFollow the steps provided right before the `From` implementation.\"\"\"\n\n[[exercises]]\nname = \"from_str\"\ndir = \"23_conversions\"\nhint = \"\"\"\nThe implementation of `FromStr` should return an `Ok` with a `Person` object,\nor an `Err` with an error if the string is not valid.\n\nThis is almost like the previous `from_into` exercise, but returning errors\ninstead of falling back to a default value.\n\nAnother hint: You can use the `map_err` method of `Result` with a function or a\nclosure to wrap the error from `parse::<u8>`.\n\nYet another hint: If you would like to propagate errors by using the `?`\noperator in your solution, you might want to look at\nhttps://doc.rust-lang.org/stable/rust-by-example/error/multiple_error_types/reenter_question_mark.html\"\"\"\n\n[[exercises]]\nname = \"try_from_into\"\ndir = \"23_conversions\"\nhint = \"\"\"\nIs there an implementation of `TryFrom` in the standard library that can both do\nthe required integer conversion and check the range of the input?\n\nChallenge: Can you make the `TryFrom` implementations generic over many integer\ntypes?\"\"\"\n\n[[exercises]]\nname = \"as_ref_mut\"\ndir = \"23_conversions\"\nhint = \"\"\"\nAdd `AsRef<str>` or `AsMut<u32>` as a trait bound to the functions.\"\"\"\n"
  },
  {
    "path": "rustlings-macros/src/lib.rs",
    "content": "use proc_macro::TokenStream;\nuse quote::quote;\nuse serde::Deserialize;\n\n#[derive(Deserialize)]\nstruct ExerciseInfo<'a> {\n    name: &'a str,\n    dir: &'a str,\n}\n\n#[derive(Deserialize)]\nstruct InfoFile<'a> {\n    #[serde(borrow)]\n    exercises: Vec<ExerciseInfo<'a>>,\n}\n\n#[proc_macro]\npub fn include_files(_: TokenStream) -> TokenStream {\n    // Remove `\\r` on Windows\n    let info_file = String::from_utf8(\n        include_bytes!(\"../info.toml\")\n            .iter()\n            .copied()\n            .filter(|c| *c != b'\\r')\n            .collect(),\n    )\n    .expect(\"Failed to parse `info.toml` as UTF8\");\n    let exercises = toml::de::from_str::<InfoFile>(&info_file)\n        .expect(\"Failed to parse `info.toml`\")\n        .exercises;\n\n    let exercise_files = exercises\n        .iter()\n        .map(|exercise| format!(\"../exercises/{}/{}.rs\", exercise.dir, exercise.name));\n    let solution_files = exercises\n        .iter()\n        .map(|exercise| format!(\"../solutions/{}/{}.rs\", exercise.dir, exercise.name));\n\n    let mut dirs = Vec::with_capacity(32);\n    let mut dir_inds = vec![0; exercises.len()];\n\n    for (exercise, dir_ind) in exercises.iter().zip(&mut dir_inds) {\n        // The directory is often the last one inserted.\n        if let Some(ind) = dirs.iter().rev().position(|dir| *dir == exercise.dir) {\n            *dir_ind = dirs.len() - 1 - ind;\n            continue;\n        }\n\n        dirs.push(exercise.dir);\n        *dir_ind = dirs.len() - 1;\n    }\n\n    let readmes = dirs\n        .iter()\n        .map(|dir| format!(\"../exercises/{dir}/README.md\"));\n\n    quote! {\n        EmbeddedFiles {\n            info_file: #info_file,\n            exercise_files: &[#(ExerciseFiles { exercise: include_bytes!(#exercise_files), solution: include_bytes!(#solution_files), dir_ind: #dir_inds }),*],\n            exercise_dirs: &[#(ExerciseDir { name: #dirs, readme: include_bytes!(#readmes) }),*]\n        }\n    }\n    .into()\n}\n"
  },
  {
    "path": "solutions/00_intro/intro1.rs",
    "content": "fn main() {\n    // Congratulations, you finished the first exercise 🎉\n    // As an introduction to Rustlings, the first exercise only required\n    // entering `n` in the terminal to go to the next exercise.\n}\n"
  },
  {
    "path": "solutions/00_intro/intro2.rs",
    "content": "fn main() {\n    // `println!` instead of `printline!`.\n    println!(\"Hello world!\");\n}\n"
  },
  {
    "path": "solutions/01_variables/variables1.rs",
    "content": "fn main() {\n    // Declaring variables requires the `let` keyword.\n    let x = 5;\n\n    println!(\"x has the value {x}\");\n}\n"
  },
  {
    "path": "solutions/01_variables/variables2.rs",
    "content": "fn main() {\n    // The easiest way to fix the compiler error is to initialize the\n    // variable `x`. By setting its value to an integer, Rust infers its type\n    // as `i32` which is the default type for integers.\n    let x = 42;\n\n    // But we can enforce a type different from the default `i32` by adding\n    // a type annotation:\n    // let x: u8 = 42;\n\n    if x == 10 {\n        println!(\"x is ten!\");\n    } else {\n        println!(\"x is not ten!\");\n    }\n}\n"
  },
  {
    "path": "solutions/01_variables/variables3.rs",
    "content": "#![allow(clippy::needless_late_init)]\n\nfn main() {\n    // Reading uninitialized variables isn't allowed in Rust!\n    // Therefore, we need to assign a value first.\n    let x: i32 = 42;\n\n    println!(\"Number {x}\");\n\n    // It is possible to declare a variable and initialize it later.\n    // But it can't be used before initialization.\n    let y: i32;\n    y = 42;\n    println!(\"Number {y}\");\n}\n"
  },
  {
    "path": "solutions/01_variables/variables4.rs",
    "content": "fn main() {\n    // In Rust, variables are immutable by default.\n    // Adding the `mut` keyword after `let` makes the declared variable mutable.\n    let mut x = 3;\n    println!(\"Number {x}\");\n\n    x = 5;\n    println!(\"Number {x}\");\n}\n"
  },
  {
    "path": "solutions/01_variables/variables5.rs",
    "content": "fn main() {\n    let number = \"T-H-R-E-E\";\n    println!(\"Spell a number: {number}\");\n\n    // Using variable shadowing\n    // https://doc.rust-lang.org/book/ch03-01-variables-and-mutability.html#shadowing\n    let number = 3;\n    println!(\"Number plus two is: {}\", number + 2);\n}\n"
  },
  {
    "path": "solutions/01_variables/variables6.rs",
    "content": "// The type of constants must always be annotated.\nconst NUMBER: u64 = 3;\n\nfn main() {\n    println!(\"Number: {NUMBER}\");\n}\n"
  },
  {
    "path": "solutions/02_functions/functions1.rs",
    "content": "// Some function with the name `call_me` without arguments or a return value.\nfn call_me() {\n    println!(\"Hello world!\");\n}\n\nfn main() {\n    call_me();\n}\n"
  },
  {
    "path": "solutions/02_functions/functions2.rs",
    "content": "// The type of function arguments must be annotated.\n// Added the type annotation `u64`.\nfn call_me(num: u64) {\n    for i in 0..num {\n        println!(\"Ring! Call number {}\", i + 1);\n    }\n}\n\nfn main() {\n    call_me(3);\n}\n"
  },
  {
    "path": "solutions/02_functions/functions3.rs",
    "content": "fn call_me(num: u8) {\n    for i in 0..num {\n        println!(\"Ring! Call number {}\", i + 1);\n    }\n}\n\nfn main() {\n    // `call_me` expects an argument.\n    call_me(5);\n}\n"
  },
  {
    "path": "solutions/02_functions/functions4.rs",
    "content": "fn is_even(num: i64) -> bool {\n    num % 2 == 0\n}\n\n// The return type must always be annotated.\nfn sale_price(price: i64) -> i64 {\n    if is_even(price) {\n        price - 10\n    } else {\n        price - 3\n    }\n}\n\nfn main() {\n    let original_price = 51;\n    println!(\"Your sale price is {}\", sale_price(original_price));\n}\n"
  },
  {
    "path": "solutions/02_functions/functions5.rs",
    "content": "fn square(num: i32) -> i32 {\n    // Removed the semicolon `;` at the end of the line below to implicitly return the result.\n    num * num\n}\n\nfn main() {\n    let answer = square(3);\n    println!(\"The square of 3 is {answer}\");\n}\n"
  },
  {
    "path": "solutions/03_if/if1.rs",
    "content": "fn bigger(a: i32, b: i32) -> i32 {\n    if a > b { a } else { b }\n}\n\nfn main() {\n    // You can optionally experiment here.\n}\n\n// Don't mind this for now :)\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    #[test]\n    fn ten_is_bigger_than_eight() {\n        assert_eq!(10, bigger(10, 8));\n    }\n\n    #[test]\n    fn fortytwo_is_bigger_than_thirtytwo() {\n        assert_eq!(42, bigger(32, 42));\n    }\n\n    #[test]\n    fn equal_numbers() {\n        assert_eq!(42, bigger(42, 42));\n    }\n}\n"
  },
  {
    "path": "solutions/03_if/if2.rs",
    "content": "fn picky_eater(food: &str) -> &str {\n    if food == \"strawberry\" {\n        \"Yummy!\"\n    } else if food == \"potato\" {\n        \"I guess I can eat that.\"\n    } else {\n        \"No thanks!\"\n    }\n}\n\nfn main() {\n    // You can optionally experiment here.\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    #[test]\n    fn yummy_food() {\n        assert_eq!(picky_eater(\"strawberry\"), \"Yummy!\");\n    }\n\n    #[test]\n    fn neutral_food() {\n        assert_eq!(picky_eater(\"potato\"), \"I guess I can eat that.\");\n    }\n\n    #[test]\n    fn default_disliked_food() {\n        assert_eq!(picky_eater(\"broccoli\"), \"No thanks!\");\n        assert_eq!(picky_eater(\"gummy bears\"), \"No thanks!\");\n        assert_eq!(picky_eater(\"literally anything\"), \"No thanks!\");\n    }\n}\n"
  },
  {
    "path": "solutions/03_if/if3.rs",
    "content": "fn animal_habitat(animal: &str) -> &str {\n    let identifier = if animal == \"crab\" {\n        1\n    } else if animal == \"gopher\" {\n        2\n    } else if animal == \"snake\" {\n        3\n    } else {\n        // Any unused identifier.\n        4\n    };\n\n    // Instead of such an identifier, you would use an enum in Rust.\n    // But we didn't get into enums yet.\n    if identifier == 1 {\n        \"Beach\"\n    } else if identifier == 2 {\n        \"Burrow\"\n    } else if identifier == 3 {\n        \"Desert\"\n    } else {\n        \"Unknown\"\n    }\n}\n\nfn main() {\n    // You can optionally experiment here.\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    #[test]\n    fn gopher_lives_in_burrow() {\n        assert_eq!(animal_habitat(\"gopher\"), \"Burrow\")\n    }\n\n    #[test]\n    fn snake_lives_in_desert() {\n        assert_eq!(animal_habitat(\"snake\"), \"Desert\")\n    }\n\n    #[test]\n    fn crab_lives_on_beach() {\n        assert_eq!(animal_habitat(\"crab\"), \"Beach\")\n    }\n\n    #[test]\n    fn unknown_animal() {\n        assert_eq!(animal_habitat(\"dinosaur\"), \"Unknown\")\n    }\n}\n"
  },
  {
    "path": "solutions/04_primitive_types/primitive_types1.rs",
    "content": "fn main() {\n    let is_morning = true;\n    if is_morning {\n        println!(\"Good morning!\");\n    }\n\n    let is_evening = !is_morning;\n    if is_evening {\n        println!(\"Good evening!\");\n    }\n}\n"
  },
  {
    "path": "solutions/04_primitive_types/primitive_types2.rs",
    "content": "fn main() {\n    let my_first_initial = 'C';\n    if my_first_initial.is_alphabetic() {\n        println!(\"Alphabetical!\");\n    } else if my_first_initial.is_numeric() {\n        println!(\"Numerical!\");\n    } else {\n        println!(\"Neither alphabetic nor numeric!\");\n    }\n\n    // Example with an emoji.\n    let your_character = '🦀';\n\n    if your_character.is_alphabetic() {\n        println!(\"Alphabetical!\");\n    } else if your_character.is_numeric() {\n        println!(\"Numerical!\");\n    } else {\n        println!(\"Neither alphabetic nor numeric!\");\n    }\n}\n"
  },
  {
    "path": "solutions/04_primitive_types/primitive_types3.rs",
    "content": "fn main() {\n    // An array with 100 elements of the value 42.\n    let a = [42; 100];\n\n    if a.len() >= 100 {\n        println!(\"Wow, that's a big array!\");\n    } else {\n        println!(\"Meh, I eat arrays like that for breakfast.\");\n        panic!(\"Array not big enough, more elements needed\");\n    }\n}\n"
  },
  {
    "path": "solutions/04_primitive_types/primitive_types4.rs",
    "content": "fn main() {\n    // You can optionally experiment here.\n}\n\n#[cfg(test)]\nmod tests {\n    #[test]\n    fn slice_out_of_array() {\n        let a = [1, 2, 3, 4, 5];\n        //       0  1  2  3  4  <- indices\n        //          -------\n        //             |\n        //             +--- slice\n\n        // Note that the upper index 4 is excluded.\n        let nice_slice = &a[1..4];\n        assert_eq!([2, 3, 4], nice_slice);\n\n        // The upper index can be included by using the syntax `..=` (with `=` sign)\n        let nice_slice = &a[1..=3];\n        assert_eq!([2, 3, 4], nice_slice);\n    }\n}\n"
  },
  {
    "path": "solutions/04_primitive_types/primitive_types5.rs",
    "content": "fn main() {\n    let cat = (\"Furry McFurson\", 3.5);\n\n    // Destructuring the tuple.\n    let (name, age) = cat;\n\n    println!(\"{name} is {age} years old\");\n}\n"
  },
  {
    "path": "solutions/04_primitive_types/primitive_types6.rs",
    "content": "fn main() {\n    // You can optionally experiment here.\n}\n\n#[cfg(test)]\nmod tests {\n    #[test]\n    fn indexing_tuple() {\n        let numbers = (1, 2, 3);\n\n        // Tuple indexing syntax.\n        let second = numbers.1;\n\n        assert_eq!(second, 2, \"This is not the 2nd number in the tuple!\");\n    }\n}\n"
  },
  {
    "path": "solutions/05_vecs/vecs1.rs",
    "content": "fn array_and_vec() -> ([i32; 4], Vec<i32>) {\n    let a = [10, 20, 30, 40]; // Array\n\n    // Used the `vec!` macro.\n    let v = vec![10, 20, 30, 40];\n\n    (a, v)\n}\n\nfn main() {\n    // You can optionally experiment here.\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    #[test]\n    fn test_array_and_vec_similarity() {\n        let (a, v) = array_and_vec();\n        assert_eq!(a, *v);\n    }\n}\n"
  },
  {
    "path": "solutions/05_vecs/vecs2.rs",
    "content": "fn vec_loop(input: &[i32]) -> Vec<i32> {\n    let mut output = Vec::new();\n\n    for element in input {\n        output.push(2 * element);\n    }\n\n    output\n}\n\nfn main() {\n    // You can optionally experiment here.\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    #[test]\n    fn test_vec_loop() {\n        let input = [2, 4, 6, 8, 10];\n        let ans = vec_loop(&input);\n        assert_eq!(ans, [4, 8, 12, 16, 20]);\n    }\n}\n"
  },
  {
    "path": "solutions/06_move_semantics/move_semantics1.rs",
    "content": "fn fill_vec(vec: Vec<i32>) -> Vec<i32> {\n    let mut vec = vec;\n    //  ^^^ added\n\n    vec.push(88);\n\n    vec\n}\n\nfn main() {\n    // You can optionally experiment here.\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    #[test]\n    fn move_semantics1() {\n        let vec0 = vec![22, 44, 66];\n        let vec1 = fill_vec(vec0);\n        // `vec0` can't be accessed anymore because it is moved to `fill_vec`.\n        assert_eq!(vec1, vec![22, 44, 66, 88]);\n    }\n}\n"
  },
  {
    "path": "solutions/06_move_semantics/move_semantics2.rs",
    "content": "fn fill_vec(vec: Vec<i32>) -> Vec<i32> {\n    let mut vec = vec;\n\n    vec.push(88);\n\n    vec\n}\n\nfn main() {\n    // You can optionally experiment here.\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    #[test]\n    fn move_semantics2() {\n        let vec0 = vec![22, 44, 66];\n\n        // Cloning `vec0` so that the clone is moved into `fill_vec`, not `vec0`\n        // itself.\n        let vec1 = fill_vec(vec0.clone());\n\n        assert_eq!(vec0, [22, 44, 66]);\n        assert_eq!(vec1, [22, 44, 66, 88]);\n    }\n}\n"
  },
  {
    "path": "solutions/06_move_semantics/move_semantics3.rs",
    "content": "fn fill_vec(mut vec: Vec<i32>) -> Vec<i32> {\n    //      ^^^ added\n    vec.push(88);\n\n    vec\n}\n\nfn main() {\n    // You can optionally experiment here.\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    #[test]\n    fn move_semantics3() {\n        let vec0 = vec![22, 44, 66];\n        let vec1 = fill_vec(vec0);\n        assert_eq!(vec1, [22, 44, 66, 88]);\n    }\n}\n"
  },
  {
    "path": "solutions/06_move_semantics/move_semantics4.rs",
    "content": "fn main() {\n    // You can optionally experiment here.\n}\n\n#[cfg(test)]\nmod tests {\n    #[test]\n    fn move_semantics4() {\n        let mut x = Vec::new();\n        let y = &mut x;\n        // `y` used here.\n        y.push(42);\n        // The mutable reference `y` is not used anymore,\n        // therefore a new reference can be created.\n        let z = &mut x;\n        z.push(13);\n        assert_eq!(x, [42, 13]);\n    }\n}\n"
  },
  {
    "path": "solutions/06_move_semantics/move_semantics5.rs",
    "content": "#![allow(clippy::ptr_arg)]\n\n// Borrows instead of taking ownership.\n// It is recommended to use `&str` instead of `&String` here. But this is\n// enough for now because we didn't handle strings yet.\nfn get_char(data: &String) -> char {\n    data.chars().last().unwrap()\n}\n\n// Takes ownership instead of borrowing.\nfn string_uppercase(mut data: String) {\n    data = data.to_uppercase();\n\n    println!(\"{data}\");\n}\n\nfn main() {\n    let data = \"Rust is great!\".to_string();\n\n    get_char(&data);\n\n    string_uppercase(data);\n}\n"
  },
  {
    "path": "solutions/07_structs/structs1.rs",
    "content": "struct ColorRegularStruct {\n    red: u8,\n    green: u8,\n    blue: u8,\n}\n\nstruct ColorTupleStruct(u8, u8, u8);\n\n#[derive(Debug)]\nstruct UnitStruct;\n\nfn main() {\n    // You can optionally experiment here.\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    #[test]\n    fn regular_structs() {\n        let green = ColorRegularStruct {\n            red: 0,\n            green: 255,\n            blue: 0,\n        };\n\n        assert_eq!(green.red, 0);\n        assert_eq!(green.green, 255);\n        assert_eq!(green.blue, 0);\n    }\n\n    #[test]\n    fn tuple_structs() {\n        let green = ColorTupleStruct(0, 255, 0);\n\n        assert_eq!(green.0, 0);\n        assert_eq!(green.1, 255);\n        assert_eq!(green.2, 0);\n    }\n\n    #[test]\n    fn unit_structs() {\n        let unit_struct = UnitStruct;\n        let message = format!(\"{unit_struct:?}s are fun!\");\n\n        assert_eq!(message, \"UnitStructs are fun!\");\n    }\n}\n"
  },
  {
    "path": "solutions/07_structs/structs2.rs",
    "content": "#[derive(Debug)]\nstruct Order {\n    name: String,\n    year: u32,\n    made_by_phone: bool,\n    made_by_mobile: bool,\n    made_by_email: bool,\n    item_number: u32,\n    count: u32,\n}\n\nfn create_order_template() -> Order {\n    Order {\n        name: String::from(\"Bob\"),\n        year: 2019,\n        made_by_phone: false,\n        made_by_mobile: false,\n        made_by_email: true,\n        item_number: 123,\n        count: 0,\n    }\n}\n\nfn main() {\n    // You can optionally experiment here.\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    #[test]\n    fn your_order() {\n        let order_template = create_order_template();\n\n        let your_order = Order {\n            name: String::from(\"Hacker in Rust\"),\n            count: 1,\n            // Struct update syntax\n            ..order_template\n        };\n\n        assert_eq!(your_order.name, \"Hacker in Rust\");\n        assert_eq!(your_order.year, order_template.year);\n        assert_eq!(your_order.made_by_phone, order_template.made_by_phone);\n        assert_eq!(your_order.made_by_mobile, order_template.made_by_mobile);\n        assert_eq!(your_order.made_by_email, order_template.made_by_email);\n        assert_eq!(your_order.item_number, order_template.item_number);\n        assert_eq!(your_order.count, 1);\n    }\n}\n"
  },
  {
    "path": "solutions/07_structs/structs3.rs",
    "content": "#[derive(Debug)]\nstruct Package {\n    sender_country: String,\n    recipient_country: String,\n    weight_in_grams: u32,\n}\n\nimpl Package {\n    fn new(sender_country: String, recipient_country: String, weight_in_grams: u32) -> Self {\n        if weight_in_grams < 10 {\n            // This isn't how you should handle errors in Rust, but we will\n            // learn about error handling later.\n            panic!(\"Can't ship a package with weight below 10 grams\");\n        }\n\n        Self {\n            sender_country,\n            recipient_country,\n            weight_in_grams,\n        }\n    }\n\n    fn is_international(&self) -> bool {\n        //                     ^^^^^^^ added\n        self.sender_country != self.recipient_country\n    }\n\n    fn get_fees(&self, cents_per_gram: u32) -> u32 {\n        //                                  ^^^^^^ added\n        self.weight_in_grams * cents_per_gram\n    }\n}\n\nfn main() {\n    // You can optionally experiment here.\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    #[test]\n    #[should_panic]\n    fn fail_creating_weightless_package() {\n        let sender_country = String::from(\"Spain\");\n        let recipient_country = String::from(\"Austria\");\n\n        Package::new(sender_country, recipient_country, 5);\n    }\n\n    #[test]\n    fn create_international_package() {\n        let sender_country = String::from(\"Spain\");\n        let recipient_country = String::from(\"Russia\");\n\n        let package = Package::new(sender_country, recipient_country, 1200);\n\n        assert!(package.is_international());\n    }\n\n    #[test]\n    fn create_local_package() {\n        let sender_country = String::from(\"Canada\");\n        let recipient_country = sender_country.clone();\n\n        let package = Package::new(sender_country, recipient_country, 1200);\n\n        assert!(!package.is_international());\n    }\n\n    #[test]\n    fn calculate_transport_fees() {\n        let sender_country = String::from(\"Spain\");\n        let recipient_country = String::from(\"Spain\");\n\n        let cents_per_gram = 3;\n\n        let package = Package::new(sender_country, recipient_country, 1500);\n\n        assert_eq!(package.get_fees(cents_per_gram), 4500);\n        assert_eq!(package.get_fees(cents_per_gram * 2), 9000);\n    }\n}\n"
  },
  {
    "path": "solutions/08_enums/enums1.rs",
    "content": "#[derive(Debug)]\nenum Message {\n    Resize,\n    Move,\n    Echo,\n    ChangeColor,\n    Quit,\n}\n\nfn main() {\n    println!(\"{:?}\", Message::Resize);\n    println!(\"{:?}\", Message::Move);\n    println!(\"{:?}\", Message::Echo);\n    println!(\"{:?}\", Message::ChangeColor);\n    println!(\"{:?}\", Message::Quit);\n}\n"
  },
  {
    "path": "solutions/08_enums/enums2.rs",
    "content": "#[derive(Debug)]\nstruct Point {\n    x: u64,\n    y: u64,\n}\n\n#[derive(Debug)]\nenum Message {\n    Resize { width: u64, height: u64 },\n    Move(Point),\n    Echo(String),\n    ChangeColor(u8, u8, u8),\n    Quit,\n}\n\nimpl Message {\n    fn call(&self) {\n        println!(\"{self:?}\");\n    }\n}\n\nfn main() {\n    let messages = [\n        Message::Resize {\n            width: 10,\n            height: 30,\n        },\n        Message::Move(Point { x: 10, y: 15 }),\n        Message::Echo(String::from(\"hello world\")),\n        Message::ChangeColor(200, 255, 255),\n        Message::Quit,\n    ];\n\n    for message in &messages {\n        message.call();\n    }\n}\n"
  },
  {
    "path": "solutions/08_enums/enums3.rs",
    "content": "struct Point {\n    x: u64,\n    y: u64,\n}\n\nenum Message {\n    Resize { width: u64, height: u64 },\n    Move(Point),\n    Echo(String),\n    ChangeColor(u8, u8, u8),\n    Quit,\n}\n\nstruct State {\n    width: u64,\n    height: u64,\n    position: Point,\n    message: String,\n    color: (u8, u8, u8),\n    quit: bool,\n}\n\nimpl State {\n    fn resize(&mut self, width: u64, height: u64) {\n        self.width = width;\n        self.height = height;\n    }\n\n    fn move_position(&mut self, point: Point) {\n        self.position = point;\n    }\n\n    fn echo(&mut self, s: String) {\n        self.message = s;\n    }\n\n    fn change_color(&mut self, red: u8, green: u8, blue: u8) {\n        self.color = (red, green, blue);\n    }\n\n    fn quit(&mut self) {\n        self.quit = true;\n    }\n\n    fn process(&mut self, message: Message) {\n        match message {\n            Message::Resize { width, height } => self.resize(width, height),\n            Message::Move(point) => self.move_position(point),\n            Message::Echo(string) => self.echo(string),\n            Message::ChangeColor(red, green, blue) => self.change_color(red, green, blue),\n            Message::Quit => self.quit(),\n        }\n    }\n}\n\nfn main() {\n    // You can optionally experiment here.\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    #[test]\n    fn test_match_message_call() {\n        let mut state = State {\n            width: 0,\n            height: 0,\n            position: Point { x: 0, y: 0 },\n            message: String::from(\"hello world\"),\n            color: (0, 0, 0),\n            quit: false,\n        };\n\n        state.process(Message::Resize {\n            width: 10,\n            height: 30,\n        });\n        state.process(Message::Move(Point { x: 10, y: 15 }));\n        state.process(Message::Echo(String::from(\"Hello world!\")));\n        state.process(Message::ChangeColor(255, 0, 255));\n        state.process(Message::Quit);\n\n        assert_eq!(state.width, 10);\n        assert_eq!(state.height, 30);\n        assert_eq!(state.position.x, 10);\n        assert_eq!(state.position.y, 15);\n        assert_eq!(state.message, \"Hello world!\");\n        assert_eq!(state.color, (255, 0, 255));\n        assert!(state.quit);\n    }\n}\n"
  },
  {
    "path": "solutions/09_strings/strings1.rs",
    "content": "fn current_favorite_color() -> String {\n    // Equivalent to `String::from(\"blue\")`\n    \"blue\".to_string()\n}\n\nfn main() {\n    let answer = current_favorite_color();\n    println!(\"My current favorite color is {answer}\");\n}\n"
  },
  {
    "path": "solutions/09_strings/strings2.rs",
    "content": "fn is_a_color_word(attempt: &str) -> bool {\n    attempt == \"green\" || attempt == \"blue\" || attempt == \"red\"\n}\n\nfn main() {\n    let word = String::from(\"green\");\n\n    if is_a_color_word(&word) {\n        //             ^ added to have `&String` which is automatically\n        //               coerced to `&str` by the compiler.\n        println!(\"That is a color word I know!\");\n    } else {\n        println!(\"That is not a color word I know.\");\n    }\n}\n"
  },
  {
    "path": "solutions/09_strings/strings3.rs",
    "content": "fn trim_me(input: &str) -> &str {\n    input.trim()\n}\n\nfn compose_me(input: &str) -> String {\n    // The macro `format!` has the same syntax as `println!`, but it returns a\n    // string instead of printing it to the terminal.\n    // Equivalent to `input.to_string() + \" world!\"`\n    format!(\"{input} world!\")\n}\n\nfn replace_me(input: &str) -> String {\n    input.replace(\"cars\", \"balloons\")\n}\n\nfn main() {\n    // You can optionally experiment here.\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    #[test]\n    fn trim_a_string() {\n        assert_eq!(trim_me(\"Hello!     \"), \"Hello!\");\n        assert_eq!(trim_me(\"  What's up!\"), \"What's up!\");\n        assert_eq!(trim_me(\"   Hola!  \"), \"Hola!\");\n        assert_eq!(trim_me(\"Hi!\"), \"Hi!\");\n    }\n\n    #[test]\n    fn compose_a_string() {\n        assert_eq!(compose_me(\"Hello\"), \"Hello world!\");\n        assert_eq!(compose_me(\"Goodbye\"), \"Goodbye world!\");\n    }\n\n    #[test]\n    fn replace_a_string() {\n        assert_eq!(\n            replace_me(\"I think cars are cool\"),\n            \"I think balloons are cool\",\n        );\n        assert_eq!(\n            replace_me(\"I love to look at cars\"),\n            \"I love to look at balloons\",\n        );\n    }\n}\n"
  },
  {
    "path": "solutions/09_strings/strings4.rs",
    "content": "fn string_slice(arg: &str) {\n    println!(\"{arg}\");\n}\n\nfn string(arg: String) {\n    println!(\"{arg}\");\n}\n\nfn main() {\n    string_slice(\"blue\");\n\n    string(\"red\".to_string());\n\n    string(String::from(\"hi\"));\n\n    string(\"rust is fun!\".to_owned());\n\n    // Here, both answers work.\n    // `.into()` converts a type into an expected type.\n    // If it is called where `String` is expected, it will convert `&str` to `String`.\n    string(\"nice weather\".into());\n    // But if it is called where `&str` is expected, then `&str` is kept as `&str` since no conversion is needed.\n    // If you remove the `#[allow(…)]` line, then Clippy will tell you to remove `.into()` below since it is a useless conversion.\n    #[allow(clippy::useless_conversion)]\n    string_slice(\"nice weather\".into());\n\n    string(format!(\"Interpolation {}\", \"Station\"));\n\n    // WARNING: This is byte indexing, not character indexing.\n    // Character indexing can be done using `s.chars().nth(INDEX)`.\n    string_slice(&String::from(\"abc\")[0..1]);\n\n    string_slice(\"  hello there \".trim());\n\n    string(\"Happy Monday!\".replace(\"Mon\", \"Tues\"));\n\n    string(\"mY sHiFt KeY iS sTiCkY\".to_lowercase());\n}\n"
  },
  {
    "path": "solutions/10_modules/modules1.rs",
    "content": "mod sausage_factory {\n    fn get_secret_recipe() -> String {\n        String::from(\"Ginger\")\n    }\n\n    // Added `pub` before `fn` to make the function accessible outside the module.\n    pub fn make_sausage() {\n        get_secret_recipe();\n        println!(\"sausage!\");\n    }\n}\n\nfn main() {\n    sausage_factory::make_sausage();\n}\n"
  },
  {
    "path": "solutions/10_modules/modules2.rs",
    "content": "mod delicious_snacks {\n    // Added `pub` and used the expected alias after `as`.\n    pub use self::fruits::PEAR as fruit;\n    pub use self::veggies::CUCUMBER as veggie;\n\n    mod fruits {\n        pub const PEAR: &str = \"Pear\";\n        pub const APPLE: &str = \"Apple\";\n    }\n\n    mod veggies {\n        pub const CUCUMBER: &str = \"Cucumber\";\n        pub const CARROT: &str = \"Carrot\";\n    }\n}\n\nfn main() {\n    println!(\n        \"favorite snacks: {} and {}\",\n        delicious_snacks::fruit,\n        delicious_snacks::veggie,\n    );\n}\n"
  },
  {
    "path": "solutions/10_modules/modules3.rs",
    "content": "use std::time::{SystemTime, UNIX_EPOCH};\n\nfn main() {\n    match SystemTime::now().duration_since(UNIX_EPOCH) {\n        Ok(n) => println!(\"1970-01-01 00:00:00 UTC was {} seconds ago!\", n.as_secs()),\n        Err(_) => panic!(\"SystemTime before UNIX EPOCH!\"),\n    }\n}\n"
  },
  {
    "path": "solutions/11_hashmaps/hashmaps1.rs",
    "content": "// A basket of fruits in the form of a hash map needs to be defined. The key\n// represents the name of the fruit and the value represents how many of that\n// particular fruit is in the basket. You have to put at least 3 different\n// types of fruits (e.g. apple, banana, mango) in the basket and the total count\n// of all the fruits should be at least 5.\n\nuse std::collections::HashMap;\n\nfn fruit_basket() -> HashMap<String, u32> {\n    // Declare the hash map.\n    let mut basket = HashMap::new();\n\n    // Two bananas are already given for you :)\n    basket.insert(String::from(\"banana\"), 2);\n\n    // Put more fruits in your basket.\n    basket.insert(String::from(\"apple\"), 3);\n    basket.insert(String::from(\"mango\"), 1);\n\n    basket\n}\n\nfn main() {\n    // You can optionally experiment here.\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    #[test]\n    fn at_least_three_types_of_fruits() {\n        let basket = fruit_basket();\n        assert!(basket.len() >= 3);\n    }\n\n    #[test]\n    fn at_least_five_fruits() {\n        let basket = fruit_basket();\n        assert!(basket.values().sum::<u32>() >= 5);\n    }\n}\n"
  },
  {
    "path": "solutions/11_hashmaps/hashmaps2.rs",
    "content": "// We're collecting different fruits to bake a delicious fruit cake. For this,\n// we have a basket, which we'll represent in the form of a hash map. The key\n// represents the name of each fruit we collect and the value represents how\n// many of that particular fruit we have collected. Three types of fruits -\n// Apple (4), Mango (2) and Lychee (5) are already in the basket hash map. You\n// must add fruit to the basket so that there is at least one of each kind and\n// more than 11 in total - we have a lot of mouths to feed. You are not allowed\n// to insert any more of the fruits that are already in the basket (Apple,\n// Mango, and Lychee).\n\nuse std::collections::HashMap;\n\n#[derive(Hash, PartialEq, Eq, Debug)]\nenum Fruit {\n    Apple,\n    Banana,\n    Mango,\n    Lychee,\n    Pineapple,\n}\n\nfn fruit_basket(basket: &mut HashMap<Fruit, u32>) {\n    let fruit_kinds = [\n        Fruit::Apple,\n        Fruit::Banana,\n        Fruit::Mango,\n        Fruit::Lychee,\n        Fruit::Pineapple,\n    ];\n\n    for fruit in fruit_kinds {\n        // If fruit doesn't exist, insert it with some value.\n        basket.entry(fruit).or_insert(5);\n    }\n}\n\nfn main() {\n    // You can optionally experiment here.\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    // Don't modify this function!\n    fn get_fruit_basket() -> HashMap<Fruit, u32> {\n        let content = [(Fruit::Apple, 4), (Fruit::Mango, 2), (Fruit::Lychee, 5)];\n        HashMap::from_iter(content)\n    }\n\n    #[test]\n    fn test_given_fruits_are_not_modified() {\n        let mut basket = get_fruit_basket();\n        fruit_basket(&mut basket);\n        assert_eq!(*basket.get(&Fruit::Apple).unwrap(), 4);\n        assert_eq!(*basket.get(&Fruit::Mango).unwrap(), 2);\n        assert_eq!(*basket.get(&Fruit::Lychee).unwrap(), 5);\n    }\n\n    #[test]\n    fn at_least_five_types_of_fruits() {\n        let mut basket = get_fruit_basket();\n        fruit_basket(&mut basket);\n        let count_fruit_kinds = basket.len();\n        assert!(count_fruit_kinds >= 5);\n    }\n\n    #[test]\n    fn greater_than_eleven_fruits() {\n        let mut basket = get_fruit_basket();\n        fruit_basket(&mut basket);\n        let count = basket.values().sum::<u32>();\n        assert!(count > 11);\n    }\n\n    #[test]\n    fn all_fruit_types_in_basket() {\n        let fruit_kinds = [\n            Fruit::Apple,\n            Fruit::Banana,\n            Fruit::Mango,\n            Fruit::Lychee,\n            Fruit::Pineapple,\n        ];\n\n        let mut basket = get_fruit_basket();\n        fruit_basket(&mut basket);\n\n        for fruit_kind in fruit_kinds {\n            let Some(amount) = basket.get(&fruit_kind) else {\n                panic!(\"Fruit kind {fruit_kind:?} was not found in basket\");\n            };\n            assert!(*amount > 0);\n        }\n    }\n}\n"
  },
  {
    "path": "solutions/11_hashmaps/hashmaps3.rs",
    "content": "// A list of scores (one per line) of a soccer match is given. Each line is of\n// the form \"<team_1_name>,<team_2_name>,<team_1_goals>,<team_2_goals>\"\n// Example: \"England,France,4,2\" (England scored 4 goals, France 2).\n//\n// You have to build a scores table containing the name of the team, the total\n// number of goals the team scored, and the total number of goals the team\n// conceded.\n\nuse std::collections::HashMap;\n\n// A structure to store the goal details of a team.\n#[derive(Default)]\nstruct TeamScores {\n    goals_scored: u8,\n    goals_conceded: u8,\n}\n\nfn build_scores_table(results: &str) -> HashMap<&str, TeamScores> {\n    // The name of the team is the key and its associated struct is the value.\n    let mut scores = HashMap::<&str, TeamScores>::new();\n\n    for line in results.lines() {\n        let mut split_iterator = line.split(',');\n        // NOTE: We use `unwrap` because we didn't deal with error handling yet.\n        let team_1_name = split_iterator.next().unwrap();\n        let team_2_name = split_iterator.next().unwrap();\n        let team_1_score: u8 = split_iterator.next().unwrap().parse().unwrap();\n        let team_2_score: u8 = split_iterator.next().unwrap().parse().unwrap();\n\n        // Insert the default with zeros if a team doesn't exist yet.\n        let team_1 = scores.entry(team_1_name).or_default();\n        // Update the values.\n        team_1.goals_scored += team_1_score;\n        team_1.goals_conceded += team_2_score;\n\n        // Similarly for the second team.\n        let team_2 = scores.entry(team_2_name).or_default();\n        team_2.goals_scored += team_2_score;\n        team_2.goals_conceded += team_1_score;\n    }\n\n    scores\n}\n\nfn main() {\n    // You can optionally experiment here.\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    const RESULTS: &str = \"England,France,4,2\nFrance,Italy,3,1\nPoland,Spain,2,0\nGermany,England,2,1\nEngland,Spain,1,0\";\n\n    #[test]\n    fn build_scores() {\n        let scores = build_scores_table(RESULTS);\n\n        assert!(\n            [\"England\", \"France\", \"Germany\", \"Italy\", \"Poland\", \"Spain\"]\n                .into_iter()\n                .all(|team_name| scores.contains_key(team_name))\n        );\n    }\n\n    #[test]\n    fn validate_team_score_1() {\n        let scores = build_scores_table(RESULTS);\n        let team = scores.get(\"England\").unwrap();\n        assert_eq!(team.goals_scored, 6);\n        assert_eq!(team.goals_conceded, 4);\n    }\n\n    #[test]\n    fn validate_team_score_2() {\n        let scores = build_scores_table(RESULTS);\n        let team = scores.get(\"Spain\").unwrap();\n        assert_eq!(team.goals_scored, 0);\n        assert_eq!(team.goals_conceded, 3);\n    }\n}\n"
  },
  {
    "path": "solutions/12_options/options1.rs",
    "content": "// This function returns how much ice cream there is left in the fridge.\n// If it's before 22:00 (24-hour system), then 5 scoops are left. At 22:00,\n// someone eats it all, so no ice cream is left (value 0). Return `None` if\n// `hour_of_day` is higher than 23.\nfn maybe_ice_cream(hour_of_day: u16) -> Option<u16> {\n    match hour_of_day {\n        0..=21 => Some(5),\n        22..=23 => Some(0),\n        _ => None,\n    }\n}\n\nfn main() {\n    // You can optionally experiment here.\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    #[test]\n    fn raw_value() {\n        // Using `unwrap` is fine in a test.\n        let ice_creams = maybe_ice_cream(12).unwrap();\n\n        assert_eq!(ice_creams, 5);\n    }\n\n    #[test]\n    fn check_ice_cream() {\n        assert_eq!(maybe_ice_cream(0), Some(5));\n        assert_eq!(maybe_ice_cream(9), Some(5));\n        assert_eq!(maybe_ice_cream(18), Some(5));\n        assert_eq!(maybe_ice_cream(22), Some(0));\n        assert_eq!(maybe_ice_cream(23), Some(0));\n        assert_eq!(maybe_ice_cream(24), None);\n        assert_eq!(maybe_ice_cream(25), None);\n    }\n}\n"
  },
  {
    "path": "solutions/12_options/options2.rs",
    "content": "fn main() {\n    // You can optionally experiment here.\n}\n\n#[cfg(test)]\nmod tests {\n    #[test]\n    fn simple_option() {\n        let target = \"rustlings\";\n        let optional_target = Some(target);\n\n        // if-let\n        if let Some(word) = optional_target {\n            assert_eq!(word, target);\n        }\n    }\n\n    #[test]\n    fn layered_option() {\n        let range = 10;\n        let mut optional_integers: Vec<Option<i8>> = vec![None];\n\n        for i in 1..=range {\n            optional_integers.push(Some(i));\n        }\n\n        let mut cursor = range;\n\n        // while-let with nested pattern matching\n        while let Some(Some(integer)) = optional_integers.pop() {\n            assert_eq!(integer, cursor);\n            cursor -= 1;\n        }\n\n        assert_eq!(cursor, 0);\n    }\n}\n"
  },
  {
    "path": "solutions/12_options/options3.rs",
    "content": "#[derive(Debug)]\nstruct Point {\n    x: i32,\n    y: i32,\n}\n\nfn main() {\n    let optional_point = Some(Point { x: 100, y: 200 });\n\n    // Solution 1: Matching over the `Option` (not `&Option`) but without moving\n    // out of the `Some` variant.\n    match optional_point {\n        Some(ref p) => println!(\"Coordinates are {},{}\", p.x, p.y),\n        //   ^^^ added\n        _ => panic!(\"No match!\"),\n    }\n\n    // Solution 2: Matching over a reference (`&Option`) by added `&` before\n    // `optional_point`.\n    match &optional_point {\n        //^ added\n        Some(p) => println!(\"Coordinates are {},{}\", p.x, p.y),\n        _ => panic!(\"No match!\"),\n    }\n\n    println!(\"{optional_point:?}\");\n}\n"
  },
  {
    "path": "solutions/13_error_handling/errors1.rs",
    "content": "fn generate_nametag_text(name: String) -> Result<String, String> {\n    //                                    ^^^^^^         ^^^^^^\n    if name.is_empty() {\n        // `Err(String)` instead of `None`.\n        Err(\"Empty names aren't allowed\".to_string())\n    } else {\n        // `Ok` instead of `Some`.\n        Ok(format!(\"Hi! My name is {name}\"))\n    }\n}\n\nfn main() {\n    // You can optionally experiment here.\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    #[test]\n    fn generates_nametag_text_for_a_nonempty_name() {\n        assert_eq!(\n            generate_nametag_text(\"Beyoncé\".to_string()).as_deref(),\n            Ok(\"Hi! My name is Beyoncé\"),\n        );\n    }\n\n    #[test]\n    fn explains_why_generating_nametag_text_fails() {\n        assert_eq!(\n            generate_nametag_text(String::new())\n                .as_ref()\n                .map_err(|e| e.as_str()),\n            Err(\"Empty names aren't allowed\"),\n        );\n    }\n}\n"
  },
  {
    "path": "solutions/13_error_handling/errors2.rs",
    "content": "// Say we're writing a game where you can buy items with tokens. All items cost\n// 5 tokens, and whenever you purchase items there is a processing fee of 1\n// token. A player of the game will type in how many items they want to buy, and\n// the `total_cost` function will calculate the total cost of the items. Since\n// the player typed in the quantity, we get it as a string. They might have\n// typed anything, not just numbers!\n//\n// Right now, this function isn't handling the error case at all. What we want\n// to do is: If we call the `total_cost` function on a string that is not a\n// number, that function will return a `ParseIntError`. In that case, we want to\n// immediately return that error from our function and not try to multiply and\n// add.\n//\n// There are at least two ways to implement this that are both correct. But one\n// is a lot shorter!\n\nuse std::num::ParseIntError;\n\n#[allow(unused_variables, clippy::question_mark)]\nfn total_cost(item_quantity: &str) -> Result<i32, ParseIntError> {\n    let processing_fee = 1;\n    let cost_per_item = 5;\n\n    // Added `?` to propagate the error.\n    let qty = item_quantity.parse::<i32>()?;\n    //                                    ^ added\n\n    // Equivalent to this verbose version:\n    let qty = match item_quantity.parse::<i32>() {\n        Ok(v) => v,\n        Err(e) => return Err(e),\n    };\n\n    Ok(qty * cost_per_item + processing_fee)\n}\n\nfn main() {\n    // You can optionally experiment here.\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n    use std::num::IntErrorKind;\n\n    #[test]\n    fn item_quantity_is_a_valid_number() {\n        assert_eq!(total_cost(\"34\"), Ok(171));\n    }\n\n    #[test]\n    fn item_quantity_is_an_invalid_number() {\n        assert_eq!(\n            total_cost(\"beep boop\").unwrap_err().kind(),\n            &IntErrorKind::InvalidDigit,\n        );\n    }\n}\n"
  },
  {
    "path": "solutions/13_error_handling/errors3.rs",
    "content": "// This is a program that is trying to use a completed version of the\n// `total_cost` function from the previous exercise. It's not working though!\n// Why not? What should we do to fix it?\n\nuse std::num::ParseIntError;\n\n// Don't change this function.\nfn total_cost(item_quantity: &str) -> Result<i32, ParseIntError> {\n    let processing_fee = 1;\n    let cost_per_item = 5;\n    let qty = item_quantity.parse::<i32>()?;\n\n    Ok(qty * cost_per_item + processing_fee)\n}\n\nfn main() -> Result<(), ParseIntError> {\n    //    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ added\n    let mut tokens = 100;\n    let pretend_user_input = \"8\";\n\n    let cost = total_cost(pretend_user_input)?;\n\n    if cost > tokens {\n        println!(\"You can't afford that many!\");\n    } else {\n        tokens -= cost;\n        println!(\"You now have {tokens} tokens.\");\n    }\n\n    // Added this line to return the `Ok` variant of the expected `Result`.\n    Ok(())\n}\n"
  },
  {
    "path": "solutions/13_error_handling/errors4.rs",
    "content": "use std::cmp::Ordering;\n\n#[derive(PartialEq, Debug)]\nenum CreationError {\n    Negative,\n    Zero,\n}\n\n#[derive(PartialEq, Debug)]\nstruct PositiveNonzeroInteger(u64);\n\nimpl PositiveNonzeroInteger {\n    fn new(value: i64) -> Result<Self, CreationError> {\n        match value.cmp(&0) {\n            Ordering::Less => Err(CreationError::Negative),\n            Ordering::Equal => Err(CreationError::Zero),\n            Ordering::Greater => Ok(Self(value as u64)),\n        }\n    }\n}\n\nfn main() {\n    // You can optionally experiment here.\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    #[test]\n    fn test_creation() {\n        assert_eq!(\n            PositiveNonzeroInteger::new(10),\n            Ok(PositiveNonzeroInteger(10)),\n        );\n        assert_eq!(\n            PositiveNonzeroInteger::new(-10),\n            Err(CreationError::Negative),\n        );\n        assert_eq!(PositiveNonzeroInteger::new(0), Err(CreationError::Zero));\n    }\n}\n"
  },
  {
    "path": "solutions/13_error_handling/errors5.rs",
    "content": "// This exercise is an altered version of the `errors4` exercise. It uses some\n// concepts that we won't get to until later in the course, like `Box` and the\n// `From` trait. It's not important to understand them in detail right now, but\n// you can read ahead if you like. For now, think of the `Box<dyn ???>` type as\n// an \"I want anything that does ???\" type.\n//\n// In short, this particular use case for boxes is for when you want to own a\n// value and you care only that it is a type which implements a particular\n// trait. To do so, the `Box` is declared as of type `Box<dyn Trait>` where\n// `Trait` is the trait the compiler looks for on any value used in that\n// context. For this exercise, that context is the potential errors which\n// can be returned in a `Result`.\n\nuse std::error::Error;\nuse std::fmt;\n\n#[derive(PartialEq, Debug)]\nenum CreationError {\n    Negative,\n    Zero,\n}\n\n// This is required so that `CreationError` can implement `Error`.\nimpl fmt::Display for CreationError {\n    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {\n        let description = match *self {\n            CreationError::Negative => \"number is negative\",\n            CreationError::Zero => \"number is zero\",\n        };\n        f.write_str(description)\n    }\n}\n\nimpl Error for CreationError {}\n\n#[derive(PartialEq, Debug)]\nstruct PositiveNonzeroInteger(u64);\n\nimpl PositiveNonzeroInteger {\n    fn new(value: i64) -> Result<PositiveNonzeroInteger, CreationError> {\n        match value {\n            x if x < 0 => Err(CreationError::Negative),\n            0 => Err(CreationError::Zero),\n            x => Ok(PositiveNonzeroInteger(x as u64)),\n        }\n    }\n}\n\nfn main() -> Result<(), Box<dyn Error>> {\n    let pretend_user_input = \"42\";\n    let x: i64 = pretend_user_input.parse()?;\n    println!(\"output={:?}\", PositiveNonzeroInteger::new(x)?);\n    Ok(())\n}\n"
  },
  {
    "path": "solutions/13_error_handling/errors6.rs",
    "content": "// Using catch-all error types like `Box<dyn Error>` isn't recommended for\n// library code where callers might want to make decisions based on the error\n// content instead of printing it out or propagating it further. Here, we define\n// a custom error type to make it possible for callers to decide what to do next\n// when our function returns an error.\n\nuse std::num::ParseIntError;\n\n#[derive(PartialEq, Debug)]\nenum CreationError {\n    Negative,\n    Zero,\n}\n\n// A custom error type that we will be using in `PositiveNonzeroInteger::parse`.\n#[derive(PartialEq, Debug)]\nenum ParsePosNonzeroError {\n    Creation(CreationError),\n    ParseInt(ParseIntError),\n}\n\nimpl ParsePosNonzeroError {\n    fn from_creation(err: CreationError) -> Self {\n        Self::Creation(err)\n    }\n\n    fn from_parse_int(err: ParseIntError) -> Self {\n        Self::ParseInt(err)\n    }\n}\n\n// As an alternative solution, implementing the `From` trait allows for the\n// automatic conversion from a `ParseIntError` into a `ParsePosNonzeroError`\n// using the `?` operator, without the need to call `map_err`.\n//\n// ```\n// let x: i64 = s.parse()?;\n// ```\n//\n// Traits like `From` will be dealt with in later exercises.\nimpl From<ParseIntError> for ParsePosNonzeroError {\n    fn from(err: ParseIntError) -> Self {\n        ParsePosNonzeroError::ParseInt(err)\n    }\n}\n\n#[derive(PartialEq, Debug)]\nstruct PositiveNonzeroInteger(u64);\n\nimpl PositiveNonzeroInteger {\n    fn new(value: i64) -> Result<Self, CreationError> {\n        match value {\n            x if x < 0 => Err(CreationError::Negative),\n            0 => Err(CreationError::Zero),\n            x => Ok(Self(x as u64)),\n        }\n    }\n\n    fn parse(s: &str) -> Result<Self, ParsePosNonzeroError> {\n        // Return an appropriate error instead of panicking when `parse()`\n        // returns an error.\n        let x: i64 = s.parse().map_err(ParsePosNonzeroError::from_parse_int)?;\n        //                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n        Self::new(x).map_err(ParsePosNonzeroError::from_creation)\n    }\n}\n\nfn main() {\n    // You can optionally experiment here.\n}\n\n#[cfg(test)]\nmod test {\n    use super::*;\n\n    #[test]\n    fn test_parse_error() {\n        assert!(matches!(\n            PositiveNonzeroInteger::parse(\"not a number\"),\n            Err(ParsePosNonzeroError::ParseInt(_)),\n        ));\n    }\n\n    #[test]\n    fn test_negative() {\n        assert_eq!(\n            PositiveNonzeroInteger::parse(\"-555\"),\n            Err(ParsePosNonzeroError::Creation(CreationError::Negative)),\n        );\n    }\n\n    #[test]\n    fn test_zero() {\n        assert_eq!(\n            PositiveNonzeroInteger::parse(\"0\"),\n            Err(ParsePosNonzeroError::Creation(CreationError::Zero)),\n        );\n    }\n\n    #[test]\n    fn test_positive() {\n        let x = PositiveNonzeroInteger::new(42).unwrap();\n        assert_eq!(x.0, 42);\n        assert_eq!(PositiveNonzeroInteger::parse(\"42\"), Ok(x));\n    }\n}\n"
  },
  {
    "path": "solutions/14_generics/generics1.rs",
    "content": "// `Vec<T>` is generic over the type `T`. In most cases, the compiler is able to\n// infer `T`, for example after pushing a value with a concrete type to the vector.\n// But in this exercise, the compiler needs some help through a type annotation.\n\nfn main() {\n    // `u8` and `i8` can both be converted to `i16`.\n    let mut numbers: Vec<i16> = Vec::new();\n    //             ^^^^^^^^^^ added\n\n    // Don't change the lines below.\n    let n1: u8 = 42;\n    numbers.push(n1.into());\n    let n2: i8 = -1;\n    numbers.push(n2.into());\n\n    println!(\"{numbers:?}\");\n}\n"
  },
  {
    "path": "solutions/14_generics/generics2.rs",
    "content": "struct Wrapper<T> {\n    value: T,\n}\n\nimpl<T> Wrapper<T> {\n    fn new(value: T) -> Self {\n        Wrapper { value }\n    }\n}\n\nfn main() {\n    // You can optionally experiment here.\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    #[test]\n    fn store_u32_in_wrapper() {\n        assert_eq!(Wrapper::new(42).value, 42);\n    }\n\n    #[test]\n    fn store_str_in_wrapper() {\n        assert_eq!(Wrapper::new(\"Foo\").value, \"Foo\");\n    }\n}\n"
  },
  {
    "path": "solutions/15_traits/traits1.rs",
    "content": "// The trait `AppendBar` has only one function which appends \"Bar\" to any object\n// implementing this trait.\ntrait AppendBar {\n    fn append_bar(self) -> Self;\n}\n\nimpl AppendBar for String {\n    fn append_bar(self) -> Self {\n        self + \"Bar\"\n    }\n}\n\nfn main() {\n    let s = String::from(\"Foo\");\n    let s = s.append_bar();\n    println!(\"s: {s}\");\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    #[test]\n    fn is_foo_bar() {\n        assert_eq!(String::from(\"Foo\").append_bar(), \"FooBar\");\n    }\n\n    #[test]\n    fn is_bar_bar() {\n        assert_eq!(String::from(\"\").append_bar().append_bar(), \"BarBar\");\n    }\n}\n"
  },
  {
    "path": "solutions/15_traits/traits2.rs",
    "content": "trait AppendBar {\n    fn append_bar(self) -> Self;\n}\n\nimpl AppendBar for Vec<String> {\n    fn append_bar(mut self) -> Self {\n        //        ^^^ this is important\n        self.push(String::from(\"Bar\"));\n        self\n    }\n}\n\nfn main() {\n    // You can optionally experiment here.\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    #[test]\n    fn is_vec_pop_eq_bar() {\n        let mut foo = vec![String::from(\"Foo\")].append_bar();\n        assert_eq!(foo.pop().unwrap(), \"Bar\");\n        assert_eq!(foo.pop().unwrap(), \"Foo\");\n    }\n}\n"
  },
  {
    "path": "solutions/15_traits/traits3.rs",
    "content": "trait Licensed {\n    fn licensing_info(&self) -> String {\n        \"Default license\".to_string()\n    }\n}\n\nstruct SomeSoftware {\n    version_number: i32,\n}\n\nstruct OtherSoftware {\n    version_number: String,\n}\n\nimpl Licensed for SomeSoftware {}\nimpl Licensed for OtherSoftware {}\n\nfn main() {\n    // You can optionally experiment here.\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    #[test]\n    fn is_licensing_info_the_same() {\n        let licensing_info = \"Default license\";\n        let some_software = SomeSoftware { version_number: 1 };\n        let other_software = OtherSoftware {\n            version_number: \"v2.0.0\".to_string(),\n        };\n        assert_eq!(some_software.licensing_info(), licensing_info);\n        assert_eq!(other_software.licensing_info(), licensing_info);\n    }\n}\n"
  },
  {
    "path": "solutions/15_traits/traits4.rs",
    "content": "trait Licensed {\n    fn licensing_info(&self) -> String {\n        \"Default license\".to_string()\n    }\n}\n\nstruct SomeSoftware;\nstruct OtherSoftware;\n\nimpl Licensed for SomeSoftware {}\nimpl Licensed for OtherSoftware {}\n\nfn compare_license_types(software1: impl Licensed, software2: impl Licensed) -> bool {\n    //                              ^^^^^^^^^^^^^             ^^^^^^^^^^^^^\n    software1.licensing_info() == software2.licensing_info()\n}\n\nfn main() {\n    // You can optionally experiment here.\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    #[test]\n    fn compare_license_information() {\n        assert!(compare_license_types(SomeSoftware, OtherSoftware));\n    }\n\n    #[test]\n    fn compare_license_information_backwards() {\n        assert!(compare_license_types(OtherSoftware, SomeSoftware));\n    }\n}\n"
  },
  {
    "path": "solutions/15_traits/traits5.rs",
    "content": "trait SomeTrait {\n    fn some_function(&self) -> bool {\n        true\n    }\n}\n\ntrait OtherTrait {\n    fn other_function(&self) -> bool {\n        true\n    }\n}\n\nstruct SomeStruct;\nimpl SomeTrait for SomeStruct {}\nimpl OtherTrait for SomeStruct {}\n\nstruct OtherStruct;\nimpl SomeTrait for OtherStruct {}\nimpl OtherTrait for OtherStruct {}\n\nfn some_func(item: impl SomeTrait + OtherTrait) -> bool {\n    //             ^^^^^^^^^^^^^^^^^^^^^^^^^^^\n    item.some_function() && item.other_function()\n}\n\nfn main() {\n    // You can optionally experiment here.\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    #[test]\n    fn test_some_func() {\n        assert!(some_func(SomeStruct));\n        assert!(some_func(OtherStruct));\n    }\n}\n"
  },
  {
    "path": "solutions/16_lifetimes/lifetimes1.rs",
    "content": "// The Rust compiler needs to know how to check whether supplied references are\n// valid, so that it can let the programmer know if a reference is at risk of\n// going out of scope before it is used. Remember, references are borrows and do\n// not own their own data. What if their owner goes out of scope?\n\nfn longest<'a>(x: &'a str, y: &'a str) -> &'a str {\n    //    ^^^^     ^^          ^^          ^^\n    if x.len() > y.len() { x } else { y }\n}\n\nfn main() {\n    // You can optionally experiment here.\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    #[test]\n    fn test_longest() {\n        assert_eq!(longest(\"abcd\", \"123\"), \"abcd\");\n        assert_eq!(longest(\"abc\", \"1234\"), \"1234\");\n    }\n}\n"
  },
  {
    "path": "solutions/16_lifetimes/lifetimes2.rs",
    "content": "fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {\n    if x.len() > y.len() { x } else { y }\n}\n\nfn main() {\n    let string1 = String::from(\"long string is long\");\n    // Solution 1: You can move `strings2` out of the inner block so that it is\n    // not dropped before the print statement.\n    let string2 = String::from(\"xyz\");\n    let result;\n    {\n        result = longest(&string1, &string2);\n    }\n    println!(\"The longest string is '{result}'\");\n    // `string2` dropped at the end of the function.\n\n    // =========================================================================\n\n    let string1 = String::from(\"long string is long\");\n    let result;\n    {\n        let string2 = String::from(\"xyz\");\n        result = longest(&string1, &string2);\n        // Solution 2: You can move the print statement into the inner block so\n        // that it is executed before `string2` is dropped.\n        println!(\"The longest string is '{result}'\");\n        // `string2` dropped here (end of the inner scope).\n    }\n}\n"
  },
  {
    "path": "solutions/16_lifetimes/lifetimes3.rs",
    "content": "// Lifetimes are also needed when structs hold references.\n\nstruct Book<'a> {\n    //     ^^^^ added a lifetime annotation\n    author: &'a str,\n    //       ^^\n    title: &'a str,\n    //      ^^\n}\n\nfn main() {\n    let book = Book {\n        author: \"George Orwell\",\n        title: \"1984\",\n    };\n\n    println!(\"{} by {}\", book.title, book.author);\n}\n"
  },
  {
    "path": "solutions/17_tests/tests1.rs",
    "content": "// Tests are important to ensure that your code does what you think it should\n// do.\n\nfn is_even(n: i64) -> bool {\n    n % 2 == 0\n}\n\nfn main() {\n    // You can optionally experiment here.\n}\n\n#[cfg(test)]\nmod tests {\n    // When writing unit tests, it is common to import everything from the outer\n    // module (`super`) using a wildcard.\n    use super::*;\n\n    #[test]\n    fn you_can_assert() {\n        assert!(is_even(0));\n        assert!(!is_even(-1));\n        //      ^ You can assert `false` using the negation operator `!`.\n    }\n}\n"
  },
  {
    "path": "solutions/17_tests/tests2.rs",
    "content": "// Calculates the power of 2 using a bit shift.\n// `1 << n` is equivalent to \"2 to the power of n\".\nfn power_of_2(n: u8) -> u64 {\n    1 << n\n}\n\nfn main() {\n    // You can optionally experiment here.\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    #[test]\n    fn you_can_assert_eq() {\n        assert_eq!(power_of_2(0), 1);\n        assert_eq!(power_of_2(1), 2);\n        assert_eq!(power_of_2(2), 4);\n        assert_eq!(power_of_2(3), 8);\n    }\n}\n"
  },
  {
    "path": "solutions/17_tests/tests3.rs",
    "content": "struct Rectangle {\n    width: i32,\n    height: i32,\n}\n\nimpl Rectangle {\n    // Don't change this function.\n    fn new(width: i32, height: i32) -> Self {\n        if width <= 0 || height <= 0 {\n            // Returning a `Result` would be better here. But we want to learn\n            // how to test functions that can panic.\n            panic!(\"Rectangle width and height must be positive\");\n        }\n\n        Rectangle { width, height }\n    }\n}\n\nfn main() {\n    // You can optionally experiment here.\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    #[test]\n    fn correct_width_and_height() {\n        let rect = Rectangle::new(10, 20);\n        assert_eq!(rect.width, 10); // Check width\n        assert_eq!(rect.height, 20); // Check height\n    }\n\n    #[test]\n    #[should_panic] // Added this attribute to check that the test panics.\n    fn negative_width() {\n        let _rect = Rectangle::new(-10, 10);\n    }\n\n    #[test]\n    #[should_panic] // Added this attribute to check that the test panics.\n    fn negative_height() {\n        let _rect = Rectangle::new(10, -10);\n    }\n}\n"
  },
  {
    "path": "solutions/18_iterators/iterators1.rs",
    "content": "// When performing operations on elements within a collection, iterators are\n// essential. This module helps you get familiar with the structure of using an\n// iterator and how to go through elements within an iterable collection.\n\nfn main() {\n    // You can optionally experiment here.\n}\n\n#[cfg(test)]\nmod tests {\n    #[test]\n    fn iterators() {\n        let my_fav_fruits = [\"banana\", \"custard apple\", \"avocado\", \"peach\", \"raspberry\"];\n\n        // Create an iterator over the array.\n        let mut fav_fruits_iterator = my_fav_fruits.iter();\n\n        assert_eq!(fav_fruits_iterator.next(), Some(&\"banana\"));\n        assert_eq!(fav_fruits_iterator.next(), Some(&\"custard apple\"));\n        assert_eq!(fav_fruits_iterator.next(), Some(&\"avocado\"));\n        assert_eq!(fav_fruits_iterator.next(), Some(&\"peach\"));\n        assert_eq!(fav_fruits_iterator.next(), Some(&\"raspberry\"));\n        assert_eq!(fav_fruits_iterator.next(), None);\n        //                                     ^^^^ reached the end\n    }\n}\n"
  },
  {
    "path": "solutions/18_iterators/iterators2.rs",
    "content": "// In this exercise, you'll learn some of the unique advantages that iterators\n// can offer.\n\n// \"hello\" -> \"Hello\"\nfn capitalize_first(input: &str) -> String {\n    let mut chars = input.chars();\n    match chars.next() {\n        None => String::new(),\n        Some(first) => first.to_uppercase().to_string() + chars.as_str(),\n    }\n}\n\n// Apply the `capitalize_first` function to a slice of string slices.\n// Return a vector of strings.\n// [\"hello\", \"world\"] -> [\"Hello\", \"World\"]\nfn capitalize_words_vector(words: &[&str]) -> Vec<String> {\n    words.iter().map(|word| capitalize_first(word)).collect()\n}\n\n// Apply the `capitalize_first` function again to a slice of string\n// slices. Return a single string.\n// [\"hello\", \" \", \"world\"] -> \"Hello World\"\nfn capitalize_words_string(words: &[&str]) -> String {\n    words.iter().map(|word| capitalize_first(word)).collect()\n}\n\nfn main() {\n    // You can optionally experiment here.\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    #[test]\n    fn test_success() {\n        assert_eq!(capitalize_first(\"hello\"), \"Hello\");\n    }\n\n    #[test]\n    fn test_empty() {\n        assert_eq!(capitalize_first(\"\"), \"\");\n    }\n\n    #[test]\n    fn test_iterate_string_vec() {\n        let words = vec![\"hello\", \"world\"];\n        assert_eq!(capitalize_words_vector(&words), [\"Hello\", \"World\"]);\n    }\n\n    #[test]\n    fn test_iterate_into_string() {\n        let words = vec![\"hello\", \" \", \"world\"];\n        assert_eq!(capitalize_words_string(&words), \"Hello World\");\n    }\n}\n"
  },
  {
    "path": "solutions/18_iterators/iterators3.rs",
    "content": "#[derive(Debug, PartialEq, Eq)]\nenum DivisionError {\n    // Example: 42 / 0\n    DivideByZero,\n    // Only case for `i64`: `i64::MIN / -1` because the result is `i64::MAX + 1`\n    IntegerOverflow,\n    // Example: 5 / 2 = 2.5\n    NotDivisible,\n}\n\nfn divide(a: i64, b: i64) -> Result<i64, DivisionError> {\n    if b == 0 {\n        return Err(DivisionError::DivideByZero);\n    }\n\n    if a == i64::MIN && b == -1 {\n        return Err(DivisionError::IntegerOverflow);\n    }\n\n    if a % b != 0 {\n        return Err(DivisionError::NotDivisible);\n    }\n\n    Ok(a / b)\n}\n\nfn result_with_list() -> Result<Vec<i64>, DivisionError> {\n    //                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n    let numbers = [27, 297, 38502, 81];\n    let division_results = numbers.into_iter().map(|n| divide(n, 27));\n    // Collects to the expected return type. Returns the first error in the\n    // division results (if one exists).\n    division_results.collect()\n}\n\nfn list_of_results() -> Vec<Result<i64, DivisionError>> {\n    //               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n    let numbers = [27, 297, 38502, 81];\n    let division_results = numbers.into_iter().map(|n| divide(n, 27));\n    // Collects to the expected return type.\n    division_results.collect()\n}\n\nfn main() {\n    // You can optionally experiment here.\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    #[test]\n    fn test_success() {\n        assert_eq!(divide(81, 9), Ok(9));\n        assert_eq!(divide(81, -1), Ok(-81));\n        assert_eq!(divide(i64::MIN, i64::MIN), Ok(1));\n    }\n\n    #[test]\n    fn test_divide_by_0() {\n        assert_eq!(divide(81, 0), Err(DivisionError::DivideByZero));\n    }\n\n    #[test]\n    fn test_integer_overflow() {\n        assert_eq!(divide(i64::MIN, -1), Err(DivisionError::IntegerOverflow));\n    }\n\n    #[test]\n    fn test_not_divisible() {\n        assert_eq!(divide(81, 6), Err(DivisionError::NotDivisible));\n    }\n\n    #[test]\n    fn test_divide_0_by_something() {\n        assert_eq!(divide(0, 81), Ok(0));\n    }\n\n    #[test]\n    fn test_result_with_list() {\n        assert_eq!(result_with_list().unwrap(), [1, 11, 1426, 3]);\n    }\n\n    #[test]\n    fn test_list_of_results() {\n        assert_eq!(list_of_results(), [Ok(1), Ok(11), Ok(1426), Ok(3)]);\n    }\n}\n"
  },
  {
    "path": "solutions/18_iterators/iterators4.rs",
    "content": "// 3 possible solutions are presented.\n\n// With `for` loop and a mutable variable.\nfn factorial_for(num: u64) -> u64 {\n    let mut result = 1;\n\n    for x in 2..=num {\n        result *= x;\n    }\n\n    result\n}\n\n// Equivalent to `factorial_for` but shorter and without a `for` loop and\n// mutable variables.\nfn factorial_fold(num: u64) -> u64 {\n    // Case num==0: The iterator 2..=0 is empty\n    //              -> The initial value of `fold` is returned which is 1.\n    // Case num==1: The iterator 2..=1 is also empty\n    //              -> The initial value 1 is returned.\n    // Case num==2: The iterator 2..=2 contains one element\n    //              -> The initial value 1 is multiplied by 2 and the result\n    //                 is returned.\n    // Case num==3: The iterator 2..=3 contains 2 elements\n    //              -> 1 * 2 is calculated, then the result 2 is multiplied by\n    //                 the second element 3 so the result 6 is returned.\n    // And so on…\n    #[allow(clippy::unnecessary_fold)]\n    (2..=num).fold(1, |acc, x| acc * x)\n}\n\n// Equivalent to `factorial_fold` but with a built-in method that is suggested\n// by Clippy.\nfn factorial_product(num: u64) -> u64 {\n    (2..=num).product()\n}\n\nfn main() {\n    // You can optionally experiment here.\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    #[test]\n    fn factorial_of_0() {\n        assert_eq!(factorial_for(0), 1);\n        assert_eq!(factorial_fold(0), 1);\n        assert_eq!(factorial_product(0), 1);\n    }\n\n    #[test]\n    fn factorial_of_1() {\n        assert_eq!(factorial_for(1), 1);\n        assert_eq!(factorial_fold(1), 1);\n        assert_eq!(factorial_product(1), 1);\n    }\n    #[test]\n    fn factorial_of_2() {\n        assert_eq!(factorial_for(2), 2);\n        assert_eq!(factorial_fold(2), 2);\n        assert_eq!(factorial_product(2), 2);\n    }\n\n    #[test]\n    fn factorial_of_4() {\n        assert_eq!(factorial_for(4), 24);\n        assert_eq!(factorial_fold(4), 24);\n        assert_eq!(factorial_product(4), 24);\n    }\n}\n"
  },
  {
    "path": "solutions/18_iterators/iterators5.rs",
    "content": "// Let's define a simple model to track Rustlings' exercise progress. Progress\n// will be modelled using a hash map. The name of the exercise is the key and\n// the progress is the value. Two counting functions were created to count the\n// number of exercises with a given progress. Recreate this counting\n// functionality using iterators. Try to not use imperative loops (for/while).\n\nuse std::collections::HashMap;\n\n#[derive(Clone, Copy, PartialEq, Eq)]\nenum Progress {\n    None,\n    Some,\n    Complete,\n}\n\nfn count_for(map: &HashMap<String, Progress>, value: Progress) -> usize {\n    let mut count = 0;\n    for val in map.values() {\n        if *val == value {\n            count += 1;\n        }\n    }\n    count\n}\n\nfn count_iterator(map: &HashMap<String, Progress>, value: Progress) -> usize {\n    // `map` is a hash map with `String` keys and `Progress` values.\n    // map = { \"variables1\": Complete, \"from_str\": None, … }\n    map.values().filter(|val| **val == value).count()\n}\n\nfn count_collection_for(collection: &[HashMap<String, Progress>], value: Progress) -> usize {\n    let mut count = 0;\n    for map in collection {\n        count += count_for(map, value);\n    }\n    count\n}\n\nfn count_collection_iterator(collection: &[HashMap<String, Progress>], value: Progress) -> usize {\n    // `collection` is a slice of hash maps.\n    // collection = [{ \"variables1\": Complete, \"from_str\": None, … },\n    //               { \"variables2\": Complete, … }, … ]\n    collection\n        .iter()\n        .map(|map| count_iterator(map, value))\n        .sum()\n}\n\n// Equivalent to `count_collection_iterator` and `count_iterator`, iterating as\n// if the collection was a single container instead of a container of containers\n// (and more accurately, a single iterator instead of an iterator of iterators).\nfn count_collection_iterator_flat(\n    collection: &[HashMap<String, Progress>],\n    value: Progress,\n) -> usize {\n    // `collection` is a slice of hash maps.\n    // collection = [{ \"variables1\": Complete, \"from_str\": None, … },\n    //               { \"variables2\": Complete, … }, … ]\n    collection\n        .iter()\n        .flat_map(HashMap::values) // or just `.flatten()` when wanting the default iterator (`HashMap::iter`)\n        .filter(|val| **val == value)\n        .count()\n}\n\nfn main() {\n    // You can optionally experiment here.\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n    use Progress::*;\n\n    fn get_map() -> HashMap<String, Progress> {\n        let mut map = HashMap::new();\n        map.insert(String::from(\"variables1\"), Complete);\n        map.insert(String::from(\"functions1\"), Complete);\n        map.insert(String::from(\"hashmap1\"), Complete);\n        map.insert(String::from(\"arc1\"), Some);\n        map.insert(String::from(\"as_ref_mut\"), None);\n        map.insert(String::from(\"from_str\"), None);\n\n        map\n    }\n\n    fn get_vec_map() -> Vec<HashMap<String, Progress>> {\n        let map = get_map();\n\n        let mut other = HashMap::new();\n        other.insert(String::from(\"variables2\"), Complete);\n        other.insert(String::from(\"functions2\"), Complete);\n        other.insert(String::from(\"if1\"), Complete);\n        other.insert(String::from(\"from_into\"), None);\n        other.insert(String::from(\"try_from_into\"), None);\n\n        vec![map, other]\n    }\n\n    #[test]\n    fn count_complete() {\n        let map = get_map();\n        assert_eq!(count_iterator(&map, Complete), 3);\n    }\n\n    #[test]\n    fn count_some() {\n        let map = get_map();\n        assert_eq!(count_iterator(&map, Some), 1);\n    }\n\n    #[test]\n    fn count_none() {\n        let map = get_map();\n        assert_eq!(count_iterator(&map, None), 2);\n    }\n\n    #[test]\n    fn count_complete_equals_for() {\n        let map = get_map();\n        let progress_states = [Complete, Some, None];\n        for progress_state in progress_states {\n            assert_eq!(\n                count_for(&map, progress_state),\n                count_iterator(&map, progress_state),\n            );\n        }\n    }\n\n    #[test]\n    fn count_collection_complete() {\n        let collection = get_vec_map();\n        assert_eq!(count_collection_iterator(&collection, Complete), 6);\n        assert_eq!(count_collection_iterator_flat(&collection, Complete), 6);\n    }\n\n    #[test]\n    fn count_collection_some() {\n        let collection = get_vec_map();\n        assert_eq!(count_collection_iterator(&collection, Some), 1);\n        assert_eq!(count_collection_iterator_flat(&collection, Some), 1);\n    }\n\n    #[test]\n    fn count_collection_none() {\n        let collection = get_vec_map();\n        assert_eq!(count_collection_iterator(&collection, None), 4);\n        assert_eq!(count_collection_iterator_flat(&collection, None), 4);\n    }\n\n    #[test]\n    fn count_collection_equals_for() {\n        let collection = get_vec_map();\n        let progress_states = [Complete, Some, None];\n\n        for progress_state in progress_states {\n            assert_eq!(\n                count_collection_for(&collection, progress_state),\n                count_collection_iterator(&collection, progress_state),\n            );\n            assert_eq!(\n                count_collection_for(&collection, progress_state),\n                count_collection_iterator_flat(&collection, progress_state),\n            );\n        }\n    }\n}\n"
  },
  {
    "path": "solutions/19_smart_pointers/arc1.rs",
    "content": "// In this exercise, we are given a `Vec` of `u32` called `numbers` with values\n// ranging from 0 to 99. We would like to use this set of numbers within 8\n// different threads simultaneously. Each thread is going to get the sum of\n// every eighth value with an offset.\n//\n// The first thread (offset 0), will sum 0, 8, 16, …\n// The second thread (offset 1), will sum 1, 9, 17, …\n// The third thread (offset 2), will sum 2, 10, 18, …\n// …\n// The eighth thread (offset 7), will sum 7, 15, 23, …\n//\n// Each thread should own a reference-counting pointer to the vector of\n// numbers. But `Rc` isn't thread-safe. Therefore, we need to use `Arc`.\n//\n// Don't get distracted by how threads are spawned and joined. We will practice\n// that later in the exercises about threads.\n\n// Don't change the lines below.\n#![forbid(unused_imports)]\nuse std::{sync::Arc, thread};\n\nfn main() {\n    let numbers: Vec<_> = (0..100u32).collect();\n\n    let shared_numbers = Arc::new(numbers);\n    //                   ^^^^^^^^^^^^^^^^^\n\n    let mut join_handles = Vec::new();\n\n    for offset in 0..8 {\n        let child_numbers = Arc::clone(&shared_numbers);\n        //                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\n        let handle = thread::spawn(move || {\n            let sum: u32 = child_numbers.iter().filter(|&&n| n % 8 == offset).sum();\n            println!(\"Sum of offset {offset} is {sum}\");\n        });\n\n        join_handles.push(handle);\n    }\n\n    for handle in join_handles.into_iter() {\n        handle.join().unwrap();\n    }\n}\n"
  },
  {
    "path": "solutions/19_smart_pointers/box1.rs",
    "content": "// At compile time, Rust needs to know how much space a type takes up. This\n// becomes problematic for recursive types, where a value can have as part of\n// itself another value of the same type. To get around the issue, we can use a\n// `Box` - a smart pointer used to store data on the heap, which also allows us\n// to wrap a recursive type.\n//\n// The recursive type we're implementing in this exercise is the \"cons list\", a\n// data structure frequently found in functional programming languages. Each\n// item in a cons list contains two elements: The value of the current item and\n// the next item. The last item is a value called `Nil`.\n\n#[derive(PartialEq, Debug)]\nenum List {\n    Cons(i32, Box<List>),\n    Nil,\n}\n\nfn create_empty_list() -> List {\n    List::Nil\n}\n\nfn create_non_empty_list() -> List {\n    List::Cons(42, Box::new(List::Nil))\n}\n\nfn main() {\n    println!(\"This is an empty cons list: {:?}\", create_empty_list());\n    println!(\n        \"This is a non-empty cons list: {:?}\",\n        create_non_empty_list(),\n    );\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    #[test]\n    fn test_create_empty_list() {\n        assert_eq!(create_empty_list(), List::Nil);\n    }\n\n    #[test]\n    fn test_create_non_empty_list() {\n        assert_ne!(create_empty_list(), create_non_empty_list());\n    }\n}\n"
  },
  {
    "path": "solutions/19_smart_pointers/cow1.rs",
    "content": "// This exercise explores the `Cow` (Clone-On-Write) smart pointer. It can\n// enclose and provide immutable access to borrowed data and clone the data\n// lazily when mutation or ownership is required. The type is designed to work\n// with general borrowed data via the `Borrow` trait.\n\nuse std::borrow::Cow;\n\nfn abs_all(input: &mut Cow<[i32]>) {\n    for ind in 0..input.len() {\n        let value = input[ind];\n        if value < 0 {\n            // Clones into a vector if not already owned.\n            input.to_mut()[ind] = -value;\n        }\n    }\n}\n\nfn main() {\n    // You can optionally experiment here.\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    #[test]\n    fn reference_mutation() {\n        // Clone occurs because `input` needs to be mutated.\n        let vec = vec![-1, 0, 1];\n        let mut input = Cow::from(&vec);\n        abs_all(&mut input);\n        assert!(matches!(input, Cow::Owned(_)));\n    }\n\n    #[test]\n    fn reference_no_mutation() {\n        // No clone occurs because `input` doesn't need to be mutated.\n        let vec = vec![0, 1, 2];\n        let mut input = Cow::from(&vec);\n        abs_all(&mut input);\n        assert!(matches!(input, Cow::Borrowed(_)));\n        //                      ^^^^^^^^^^^^^^^^\n    }\n\n    #[test]\n    fn owned_no_mutation() {\n        // We can also pass `vec` without `&` so `Cow` owns it directly. In this\n        // case, no mutation occurs (all numbers are already absolute) and thus\n        // also no clone. But the result is still owned because it was never\n        // borrowed or mutated.\n        let vec = vec![0, 1, 2];\n        let mut input = Cow::from(vec);\n        abs_all(&mut input);\n        assert!(matches!(input, Cow::Owned(_)));\n        //                      ^^^^^^^^^^^^^\n    }\n\n    #[test]\n    fn owned_mutation() {\n        // Of course this is also the case if a mutation does occur (not all\n        // numbers are absolute). In this case, the call to `to_mut()` in the\n        // `abs_all` function returns a reference to the same data as before.\n        let vec = vec![-1, 0, 1];\n        let mut input = Cow::from(vec);\n        abs_all(&mut input);\n        assert!(matches!(input, Cow::Owned(_)));\n        //                      ^^^^^^^^^^^^^\n    }\n}\n"
  },
  {
    "path": "solutions/19_smart_pointers/rc1.rs",
    "content": "// In this exercise, we want to express the concept of multiple owners via the\n// `Rc<T>` type. This is a model of our solar system - there is a `Sun` type and\n// multiple `Planet`s. The planets take ownership of the sun, indicating that\n// they revolve around the sun.\n\nuse std::rc::Rc;\n\n#[derive(Debug)]\nstruct Sun;\n\n#[derive(Debug)]\nenum Planet {\n    Mercury(Rc<Sun>),\n    Venus(Rc<Sun>),\n    Earth(Rc<Sun>),\n    Mars(Rc<Sun>),\n    Jupiter(Rc<Sun>),\n    Saturn(Rc<Sun>),\n    Uranus(Rc<Sun>),\n    Neptune(Rc<Sun>),\n}\n\nimpl Planet {\n    fn details(&self) {\n        println!(\"Hi from {self:?}!\");\n    }\n}\n\nfn main() {\n    // You can optionally experiment here.\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    #[test]\n    fn rc1() {\n        let sun = Rc::new(Sun);\n        println!(\"reference count = {}\", Rc::strong_count(&sun)); // 1 reference\n\n        let mercury = Planet::Mercury(Rc::clone(&sun));\n        println!(\"reference count = {}\", Rc::strong_count(&sun)); // 2 references\n        mercury.details();\n\n        let venus = Planet::Venus(Rc::clone(&sun));\n        println!(\"reference count = {}\", Rc::strong_count(&sun)); // 3 references\n        venus.details();\n\n        let earth = Planet::Earth(Rc::clone(&sun));\n        println!(\"reference count = {}\", Rc::strong_count(&sun)); // 4 references\n        earth.details();\n\n        let mars = Planet::Mars(Rc::clone(&sun));\n        println!(\"reference count = {}\", Rc::strong_count(&sun)); // 5 references\n        mars.details();\n\n        let jupiter = Planet::Jupiter(Rc::clone(&sun));\n        println!(\"reference count = {}\", Rc::strong_count(&sun)); // 6 references\n        jupiter.details();\n\n        let saturn = Planet::Saturn(Rc::clone(&sun));\n        println!(\"reference count = {}\", Rc::strong_count(&sun)); // 7 references\n        saturn.details();\n\n        let uranus = Planet::Uranus(Rc::clone(&sun));\n        println!(\"reference count = {}\", Rc::strong_count(&sun)); // 8 references\n        uranus.details();\n\n        let neptune = Planet::Neptune(Rc::clone(&sun));\n        println!(\"reference count = {}\", Rc::strong_count(&sun)); // 9 references\n        neptune.details();\n\n        assert_eq!(Rc::strong_count(&sun), 9);\n\n        drop(neptune);\n        println!(\"reference count = {}\", Rc::strong_count(&sun)); // 8 references\n\n        drop(uranus);\n        println!(\"reference count = {}\", Rc::strong_count(&sun)); // 7 references\n\n        drop(saturn);\n        println!(\"reference count = {}\", Rc::strong_count(&sun)); // 6 references\n\n        drop(jupiter);\n        println!(\"reference count = {}\", Rc::strong_count(&sun)); // 5 references\n\n        drop(mars);\n        println!(\"reference count = {}\", Rc::strong_count(&sun)); // 4 references\n\n        drop(earth);\n        println!(\"reference count = {}\", Rc::strong_count(&sun)); // 3 references\n\n        drop(venus);\n        println!(\"reference count = {}\", Rc::strong_count(&sun)); // 2 references\n\n        drop(mercury);\n        println!(\"reference count = {}\", Rc::strong_count(&sun)); // 1 reference\n\n        assert_eq!(Rc::strong_count(&sun), 1);\n    }\n}\n"
  },
  {
    "path": "solutions/20_threads/threads1.rs",
    "content": "// This program spawns multiple threads that each runs for at least 250ms, and\n// each thread returns how much time it took to complete. The program should\n// wait until all the spawned threads have finished and should collect their\n// return values into a vector.\n\nuse std::{\n    thread,\n    time::{Duration, Instant},\n};\n\nfn main() {\n    let mut handles = Vec::new();\n    for i in 0..10 {\n        let handle = thread::spawn(move || {\n            let start = Instant::now();\n            thread::sleep(Duration::from_millis(250));\n            println!(\"Thread {i} done\");\n            start.elapsed().as_millis()\n        });\n        handles.push(handle);\n    }\n\n    let mut results = Vec::new();\n    for handle in handles {\n        // Collect the results of all threads into the `results` vector.\n        results.push(handle.join().unwrap());\n    }\n\n    if results.len() != 10 {\n        panic!(\"Oh no! Some thread isn't done yet!\");\n    }\n\n    println!();\n    for (i, result) in results.into_iter().enumerate() {\n        println!(\"Thread {i} took {result}ms\");\n    }\n}\n"
  },
  {
    "path": "solutions/20_threads/threads2.rs",
    "content": "// Building on the last exercise, we want all of the threads to complete their\n// work. But this time, the spawned threads need to be in charge of updating a\n// shared value: `JobStatus.jobs_done`\n\nuse std::{\n    sync::{Arc, Mutex},\n    thread,\n    time::Duration,\n};\n\nstruct JobStatus {\n    jobs_done: u32,\n}\n\nfn main() {\n    // `Arc` isn't enough if you want a **mutable** shared state.\n    // We need to wrap the value with a `Mutex`.\n    let status = Arc::new(Mutex::new(JobStatus { jobs_done: 0 }));\n    //                    ^^^^^^^^^^^                          ^\n\n    let mut handles = Vec::new();\n    for _ in 0..10 {\n        let status_shared = Arc::clone(&status);\n        let handle = thread::spawn(move || {\n            thread::sleep(Duration::from_millis(250));\n\n            // Lock before you update a shared value.\n            status_shared.lock().unwrap().jobs_done += 1;\n            //           ^^^^^^^^^^^^^^^^\n        });\n        handles.push(handle);\n    }\n\n    // Waiting for all jobs to complete.\n    for handle in handles {\n        handle.join().unwrap();\n    }\n\n    println!(\"Jobs done: {}\", status.lock().unwrap().jobs_done);\n    //                        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n}\n"
  },
  {
    "path": "solutions/20_threads/threads3.rs",
    "content": "use std::{sync::mpsc, thread, time::Duration};\n\nstruct Queue {\n    first_half: Vec<u32>,\n    second_half: Vec<u32>,\n}\n\nimpl Queue {\n    fn new() -> Self {\n        Self {\n            first_half: vec![1, 2, 3, 4, 5],\n            second_half: vec![6, 7, 8, 9, 10],\n        }\n    }\n}\n\nfn send_tx(q: Queue, tx: mpsc::Sender<u32>) {\n    // Clone the sender `tx` first.\n    let tx_clone = tx.clone();\n    thread::spawn(move || {\n        for val in q.first_half {\n            println!(\"Sending {val:?}\");\n            // Then use the clone in the first thread. This means that\n            // `tx_clone` is moved to the first thread and `tx` to the second.\n            tx_clone.send(val).unwrap();\n            thread::sleep(Duration::from_millis(250));\n        }\n    });\n\n    thread::spawn(move || {\n        for val in q.second_half {\n            println!(\"Sending {val:?}\");\n            tx.send(val).unwrap();\n            thread::sleep(Duration::from_millis(250));\n        }\n    });\n}\n\nfn main() {\n    // You can optionally experiment here.\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    #[test]\n    fn threads3() {\n        let (tx, rx) = mpsc::channel();\n        let queue = Queue::new();\n\n        send_tx(queue, tx);\n\n        let mut received = Vec::with_capacity(10);\n        for value in rx {\n            received.push(value);\n        }\n\n        received.sort();\n        assert_eq!(received, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);\n    }\n}\n"
  },
  {
    "path": "solutions/21_macros/macros1.rs",
    "content": "macro_rules! my_macro {\n    () => {\n        println!(\"Check out my macro!\");\n    };\n}\n\nfn main() {\n    my_macro!();\n    //      ^\n}\n"
  },
  {
    "path": "solutions/21_macros/macros2.rs",
    "content": "// Moved the macro definition to be before its call.\nmacro_rules! my_macro {\n    () => {\n        println!(\"Check out my macro!\");\n    };\n}\n\nfn main() {\n    my_macro!();\n}\n"
  },
  {
    "path": "solutions/21_macros/macros3.rs",
    "content": "// Added the `macro_use` attribute.\n#[macro_use]\nmod macros {\n    macro_rules! my_macro {\n        () => {\n            println!(\"Check out my macro!\");\n        };\n    }\n}\n\nfn main() {\n    my_macro!();\n}\n"
  },
  {
    "path": "solutions/21_macros/macros4.rs",
    "content": "// Added semicolons to separate the macro arms.\n#[rustfmt::skip]\nmacro_rules! my_macro {\n    () => {\n        println!(\"Check out my macro!\");\n    };\n    ($val:expr) => {\n        println!(\"Look at this other macro: {}\", $val);\n    };\n}\n\nfn main() {\n    my_macro!();\n    my_macro!(7777);\n}\n"
  },
  {
    "path": "solutions/22_clippy/clippy1.rs",
    "content": "// The Clippy tool is a collection of lints to analyze your code so you can\n// catch common mistakes and improve your Rust code.\n//\n// For these exercises, the code will fail to compile when there are Clippy\n// warnings. Check Clippy's suggestions from the output to solve the exercise.\n\nuse std::f32::consts::PI;\n\nfn main() {\n    // Use the more accurate `PI` constant.\n    let pi = PI;\n    let radius: f32 = 5.0;\n\n    let area = pi * radius.powi(2);\n\n    println!(\"The area of a circle with radius {radius:.2} is {area:.5}\");\n}\n"
  },
  {
    "path": "solutions/22_clippy/clippy2.rs",
    "content": "fn main() {\n    let mut res = 42;\n    let option = Some(12);\n    // Use `if-let` instead of iteration.\n    if let Some(x) = option {\n        res += x;\n    }\n\n    println!(\"{res}\");\n}\n"
  },
  {
    "path": "solutions/22_clippy/clippy3.rs",
    "content": "use std::mem;\n\n#[allow(unused_variables, unused_assignments)]\nfn main() {\n    let my_option: Option<&str> = None;\n    // `unwrap` of an `Option` after checking if it is `None` will panic.\n    // Use `if-let` instead.\n    if let Some(value) = my_option {\n        println!(\"{value}\");\n    }\n\n    // A comma was missing.\n    #[rustfmt::skip]\n    let my_arr = &[\n        -1, -2, -3,\n        -4, -5, -6,\n    ];\n    println!(\"My array! Here it is: {my_arr:?}\");\n\n    let mut my_vec = vec![1, 2, 3, 4, 5];\n    // `resize` mutates a vector instead of returning a new one.\n    // `resize(0, …)` clears a vector, so it is better to use `clear`.\n    my_vec.clear();\n    println!(\"This Vec is empty, see? {my_vec:?}\");\n\n    let mut value_a = 45;\n    let mut value_b = 66;\n    // Use `mem::swap` to correctly swap two values.\n    mem::swap(&mut value_a, &mut value_b);\n    println!(\"value a: {value_a}; value b: {value_b}\");\n}\n"
  },
  {
    "path": "solutions/23_conversions/as_ref_mut.rs",
    "content": "// AsRef and AsMut allow for cheap reference-to-reference conversions. Read more\n// about them at https://doc.rust-lang.org/std/convert/trait.AsRef.html and\n// https://doc.rust-lang.org/std/convert/trait.AsMut.html, respectively.\n\n// Obtain the number of bytes (not characters) in the given argument\n// (`.len()` returns the number of bytes in a string).\nfn byte_counter<T: AsRef<str>>(arg: T) -> usize {\n    arg.as_ref().len()\n}\n\n// Obtain the number of characters (not bytes) in the given argument.\nfn char_counter<T: AsRef<str>>(arg: T) -> usize {\n    arg.as_ref().chars().count()\n}\n\n// Squares a number using `as_mut()`.\nfn num_sq<T: AsMut<u32>>(arg: &mut T) {\n    let arg = arg.as_mut();\n    *arg *= *arg;\n}\n\nfn main() {\n    // You can optionally experiment here.\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    #[test]\n    fn different_counts() {\n        let s = \"Café au lait\";\n        assert_ne!(char_counter(s), byte_counter(s));\n    }\n\n    #[test]\n    fn same_counts() {\n        let s = \"Cafe au lait\";\n        assert_eq!(char_counter(s), byte_counter(s));\n    }\n\n    #[test]\n    fn different_counts_using_string() {\n        let s = String::from(\"Café au lait\");\n        assert_ne!(char_counter(s.clone()), byte_counter(s));\n    }\n\n    #[test]\n    fn same_counts_using_string() {\n        let s = String::from(\"Cafe au lait\");\n        assert_eq!(char_counter(s.clone()), byte_counter(s));\n    }\n\n    #[test]\n    fn mut_box() {\n        let mut num: Box<u32> = Box::new(3);\n        num_sq(&mut num);\n        assert_eq!(*num, 9);\n    }\n}\n"
  },
  {
    "path": "solutions/23_conversions/from_into.rs",
    "content": "// The `From` trait is used for value-to-value conversions. If `From` is\n// implemented, an implementation of `Into` is automatically provided.\n// You can read more about it in the documentation:\n// https://doc.rust-lang.org/std/convert/trait.From.html\n\n#[derive(Debug)]\nstruct Person {\n    name: String,\n    age: u8,\n}\n\n// We implement the Default trait to use it as a fallback when the provided\n// string is not convertible into a `Person` object.\nimpl Default for Person {\n    fn default() -> Self {\n        Self {\n            name: String::from(\"John\"),\n            age: 30,\n        }\n    }\n}\n\nimpl From<&str> for Person {\n    fn from(s: &str) -> Self {\n        let mut split = s.split(',');\n        let (Some(name), Some(age), None) = (split.next(), split.next(), split.next()) else {\n            //                      ^^^^ there should be no third element\n            return Self::default();\n        };\n\n        if name.is_empty() {\n            return Self::default();\n        }\n\n        let Ok(age) = age.parse() else {\n            return Self::default();\n        };\n\n        Self {\n            name: name.into(),\n            age,\n        }\n    }\n}\n\nfn main() {\n    // Use the `from` function.\n    let p1 = Person::from(\"Mark,20\");\n    println!(\"{p1:?}\");\n\n    // Since `From` is implemented for Person, we are able to use `Into`.\n    let p2: Person = \"Gerald,70\".into();\n    println!(\"{p2:?}\");\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    #[test]\n    fn test_default() {\n        let dp = Person::default();\n        assert_eq!(dp.name, \"John\");\n        assert_eq!(dp.age, 30);\n    }\n\n    #[test]\n    fn test_bad_convert() {\n        let p = Person::from(\"\");\n        assert_eq!(p.name, \"John\");\n        assert_eq!(p.age, 30);\n    }\n\n    #[test]\n    fn test_good_convert() {\n        let p = Person::from(\"Mark,20\");\n        assert_eq!(p.name, \"Mark\");\n        assert_eq!(p.age, 20);\n    }\n\n    #[test]\n    fn test_bad_age() {\n        let p = Person::from(\"Mark,twenty\");\n        assert_eq!(p.name, \"John\");\n        assert_eq!(p.age, 30);\n    }\n\n    #[test]\n    fn test_missing_comma_and_age() {\n        let p: Person = Person::from(\"Mark\");\n        assert_eq!(p.name, \"John\");\n        assert_eq!(p.age, 30);\n    }\n\n    #[test]\n    fn test_missing_age() {\n        let p: Person = Person::from(\"Mark,\");\n        assert_eq!(p.name, \"John\");\n        assert_eq!(p.age, 30);\n    }\n\n    #[test]\n    fn test_missing_name() {\n        let p: Person = Person::from(\",1\");\n        assert_eq!(p.name, \"John\");\n        assert_eq!(p.age, 30);\n    }\n\n    #[test]\n    fn test_missing_name_and_age() {\n        let p: Person = Person::from(\",\");\n        assert_eq!(p.name, \"John\");\n        assert_eq!(p.age, 30);\n    }\n\n    #[test]\n    fn test_missing_name_and_invalid_age() {\n        let p: Person = Person::from(\",one\");\n        assert_eq!(p.name, \"John\");\n        assert_eq!(p.age, 30);\n    }\n\n    #[test]\n    fn test_trailing_comma() {\n        let p: Person = Person::from(\"Mike,32,\");\n        assert_eq!(p.name, \"John\");\n        assert_eq!(p.age, 30);\n    }\n\n    #[test]\n    fn test_trailing_comma_and_some_string() {\n        let p: Person = Person::from(\"Mike,32,dog\");\n        assert_eq!(p.name, \"John\");\n        assert_eq!(p.age, 30);\n    }\n}\n"
  },
  {
    "path": "solutions/23_conversions/from_str.rs",
    "content": "// This is similar to the previous `from_into` exercise. But this time, we'll\n// implement `FromStr` and return errors instead of falling back to a default\n// value. Additionally, upon implementing `FromStr`, you can use the `parse`\n// method on strings to generate an object of the implementor type. You can read\n// more about it in the documentation:\n// https://doc.rust-lang.org/std/str/trait.FromStr.html\n\nuse std::num::ParseIntError;\nuse std::str::FromStr;\n\n#[derive(Debug, PartialEq)]\nstruct Person {\n    name: String,\n    age: u8,\n}\n\n// We will use this error type for the `FromStr` implementation.\n#[derive(Debug, PartialEq)]\nenum ParsePersonError {\n    // Incorrect number of fields\n    BadLen,\n    // Empty name field\n    NoName,\n    // Wrapped error from parse::<u8>()\n    ParseInt(ParseIntError),\n}\n\nimpl FromStr for Person {\n    type Err = ParsePersonError;\n\n    fn from_str(s: &str) -> Result<Self, Self::Err> {\n        let mut split = s.split(',');\n        let (Some(name), Some(age), None) = (split.next(), split.next(), split.next()) else {\n            //                      ^^^^ there should be no third element\n            return Err(ParsePersonError::BadLen);\n        };\n\n        if name.is_empty() {\n            return Err(ParsePersonError::NoName);\n        }\n\n        let age = age.parse().map_err(ParsePersonError::ParseInt)?;\n\n        Ok(Self {\n            name: name.into(),\n            age,\n        })\n    }\n}\n\nfn main() {\n    let p = \"Mark,20\".parse::<Person>();\n    println!(\"{p:?}\");\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n    use ParsePersonError::*;\n\n    #[test]\n    fn empty_input() {\n        assert_eq!(\"\".parse::<Person>(), Err(BadLen));\n    }\n\n    #[test]\n    fn good_input() {\n        let p = \"John,32\".parse::<Person>();\n        assert!(p.is_ok());\n        let p = p.unwrap();\n        assert_eq!(p.name, \"John\");\n        assert_eq!(p.age, 32);\n    }\n\n    #[test]\n    fn missing_age() {\n        assert!(matches!(\"John,\".parse::<Person>(), Err(ParseInt(_))));\n    }\n\n    #[test]\n    fn invalid_age() {\n        assert!(matches!(\"John,twenty\".parse::<Person>(), Err(ParseInt(_))));\n    }\n\n    #[test]\n    fn missing_comma_and_age() {\n        assert_eq!(\"John\".parse::<Person>(), Err(BadLen));\n    }\n\n    #[test]\n    fn missing_name() {\n        assert_eq!(\",1\".parse::<Person>(), Err(NoName));\n    }\n\n    #[test]\n    fn missing_name_and_age() {\n        assert!(matches!(\",\".parse::<Person>(), Err(NoName | ParseInt(_))));\n    }\n\n    #[test]\n    fn missing_name_and_invalid_age() {\n        assert!(matches!(\n            \",one\".parse::<Person>(),\n            Err(NoName | ParseInt(_)),\n        ));\n    }\n\n    #[test]\n    fn trailing_comma() {\n        assert_eq!(\"John,32,\".parse::<Person>(), Err(BadLen));\n    }\n\n    #[test]\n    fn trailing_comma_and_some_string() {\n        assert_eq!(\"John,32,man\".parse::<Person>(), Err(BadLen));\n    }\n}\n"
  },
  {
    "path": "solutions/23_conversions/try_from_into.rs",
    "content": "// `TryFrom` is a simple and safe type conversion that may fail in a controlled\n// way under some circumstances. Basically, this is the same as `From`. The main\n// difference is that this should return a `Result` type instead of the target\n// type itself. You can read more about it in the documentation:\n// https://doc.rust-lang.org/std/convert/trait.TryFrom.html\n\n#![allow(clippy::useless_vec)]\nuse std::convert::{TryFrom, TryInto};\n\n#[derive(Debug, PartialEq)]\nstruct Color {\n    red: u8,\n    green: u8,\n    blue: u8,\n}\n\n// We will use this error type for the `TryFrom` conversions.\n#[derive(Debug, PartialEq)]\nenum IntoColorError {\n    // Incorrect length of slice\n    BadLen,\n    // Integer conversion error\n    IntConversion,\n}\n\nimpl TryFrom<(i16, i16, i16)> for Color {\n    type Error = IntoColorError;\n\n    fn try_from(tuple: (i16, i16, i16)) -> Result<Self, Self::Error> {\n        let (Ok(red), Ok(green), Ok(blue)) = (\n            u8::try_from(tuple.0),\n            u8::try_from(tuple.1),\n            u8::try_from(tuple.2),\n        ) else {\n            return Err(IntoColorError::IntConversion);\n        };\n\n        Ok(Self { red, green, blue })\n    }\n}\n\nimpl TryFrom<[i16; 3]> for Color {\n    type Error = IntoColorError;\n\n    fn try_from(arr: [i16; 3]) -> Result<Self, Self::Error> {\n        // Reuse the implementation for a tuple.\n        Self::try_from((arr[0], arr[1], arr[2]))\n    }\n}\n\nimpl TryFrom<&[i16]> for Color {\n    type Error = IntoColorError;\n\n    fn try_from(slice: &[i16]) -> Result<Self, Self::Error> {\n        // Check the length.\n        if slice.len() != 3 {\n            return Err(IntoColorError::BadLen);\n        }\n\n        // Reuse the implementation for a tuple.\n        Self::try_from((slice[0], slice[1], slice[2]))\n    }\n}\n\nfn main() {\n    // Using the `try_from` function.\n    let c1 = Color::try_from((183, 65, 14));\n    println!(\"{c1:?}\");\n\n    // Since `TryFrom` is implemented for `Color`, we can use `TryInto`.\n    let c2: Result<Color, _> = [183, 65, 14].try_into();\n    println!(\"{c2:?}\");\n\n    let v = vec![183, 65, 14];\n    // With slice we should use the `try_from` function\n    let c3 = Color::try_from(&v[..]);\n    println!(\"{c3:?}\");\n    // or put the slice within round brackets and use `try_into`.\n    let c4: Result<Color, _> = (&v[..]).try_into();\n    println!(\"{c4:?}\");\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n    use IntoColorError::*;\n\n    #[test]\n    fn test_tuple_out_of_range_positive() {\n        assert_eq!(Color::try_from((256, 1000, 10000)), Err(IntConversion));\n    }\n\n    #[test]\n    fn test_tuple_out_of_range_negative() {\n        assert_eq!(Color::try_from((-1, -10, -256)), Err(IntConversion));\n    }\n\n    #[test]\n    fn test_tuple_sum() {\n        assert_eq!(Color::try_from((-1, 255, 255)), Err(IntConversion));\n    }\n\n    #[test]\n    fn test_tuple_correct() {\n        let c: Result<Color, _> = (183, 65, 14).try_into();\n        assert!(c.is_ok());\n        assert_eq!(\n            c.unwrap(),\n            Color {\n                red: 183,\n                green: 65,\n                blue: 14,\n            }\n        );\n    }\n\n    #[test]\n    fn test_array_out_of_range_positive() {\n        let c: Result<Color, _> = [1000, 10000, 256].try_into();\n        assert_eq!(c, Err(IntConversion));\n    }\n\n    #[test]\n    fn test_array_out_of_range_negative() {\n        let c: Result<Color, _> = [-10, -256, -1].try_into();\n        assert_eq!(c, Err(IntConversion));\n    }\n\n    #[test]\n    fn test_array_sum() {\n        let c: Result<Color, _> = [-1, 255, 255].try_into();\n        assert_eq!(c, Err(IntConversion));\n    }\n\n    #[test]\n    fn test_array_correct() {\n        let c: Result<Color, _> = [183, 65, 14].try_into();\n        assert!(c.is_ok());\n        assert_eq!(\n            c.unwrap(),\n            Color {\n                red: 183,\n                green: 65,\n                blue: 14\n            }\n        );\n    }\n\n    #[test]\n    fn test_slice_out_of_range_positive() {\n        let arr = [10000, 256, 1000];\n        assert_eq!(Color::try_from(&arr[..]), Err(IntConversion));\n    }\n\n    #[test]\n    fn test_slice_out_of_range_negative() {\n        let arr = [-256, -1, -10];\n        assert_eq!(Color::try_from(&arr[..]), Err(IntConversion));\n    }\n\n    #[test]\n    fn test_slice_sum() {\n        let arr = [-1, 255, 255];\n        assert_eq!(Color::try_from(&arr[..]), Err(IntConversion));\n    }\n\n    #[test]\n    fn test_slice_correct() {\n        let v = vec![183, 65, 14];\n        let c: Result<Color, _> = Color::try_from(&v[..]);\n        assert!(c.is_ok());\n        assert_eq!(\n            c.unwrap(),\n            Color {\n                red: 183,\n                green: 65,\n                blue: 14,\n            }\n        );\n    }\n\n    #[test]\n    fn test_slice_excess_length() {\n        let v = vec![0, 0, 0, 0];\n        assert_eq!(Color::try_from(&v[..]), Err(BadLen));\n    }\n\n    #[test]\n    fn test_slice_insufficient_length() {\n        let v = vec![0, 0];\n        assert_eq!(Color::try_from(&v[..]), Err(BadLen));\n    }\n}\n"
  },
  {
    "path": "solutions/23_conversions/using_as.rs",
    "content": "// Type casting in Rust is done via the usage of the `as` operator.\n// Note that the `as` operator is not only used when type casting. It also helps\n// with renaming imports.\n\nfn average(values: &[f64]) -> f64 {\n    let total = values.iter().sum::<f64>();\n    total / values.len() as f64\n    //                   ^^^^^^\n}\n\nfn main() {\n    let values = [3.5, 0.3, 13.0, 11.7];\n    println!(\"{}\", average(&values));\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    #[test]\n    fn returns_proper_type_and_value() {\n        assert_eq!(average(&[3.5, 0.3, 13.0, 11.7]), 7.125);\n    }\n}\n"
  },
  {
    "path": "solutions/README.md",
    "content": "# Official Rustlings solutions\n\nBefore you finish an exercise, its solution file will only contain an empty `main` function.\nThe content of this file will be automatically replaced by the actual solution once you finish the exercise.\n\nNote that these solutions are often only _one possibility_ to solve an exercise.\n"
  },
  {
    "path": "solutions/quizzes/quiz1.rs",
    "content": "// Mary is buying apples. The price of an apple is calculated as follows:\n// - An apple costs 2 rustbucks.\n// - However, if Mary buys more than 40 apples, the price of each apple in the\n// entire order is reduced to only 1 rustbuck!\n\nfn calculate_price_of_apples(n_apples: u64) -> u64 {\n    if n_apples > 40 {\n        n_apples\n    } else {\n        2 * n_apples\n    }\n}\n\nfn main() {\n    // You can optionally experiment here.\n}\n\n// Don't change the tests!\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    #[test]\n    fn verify_test() {\n        assert_eq!(calculate_price_of_apples(35), 70);\n        assert_eq!(calculate_price_of_apples(40), 80);\n        assert_eq!(calculate_price_of_apples(41), 41);\n        assert_eq!(calculate_price_of_apples(65), 65);\n    }\n}\n"
  },
  {
    "path": "solutions/quizzes/quiz2.rs",
    "content": "// Let's build a little machine in the form of a function. As input, we're going\n// to give a list of strings and commands. These commands determine what action\n// is going to be applied to the string. It can either be:\n// - Uppercase the string\n// - Trim the string\n// - Append \"bar\" to the string a specified amount of times\n//\n// The exact form of this will be:\n// - The input is going to be a vector of 2-length tuples,\n//   the first element is the string, the second one is the command.\n// - The output element is going to be a vector of strings.\n\nenum Command {\n    Uppercase,\n    Trim,\n    Append(usize),\n}\n\nmod my_module {\n    use super::Command;\n\n    // The solution with a loop. Check out `transformer_iter` for a version\n    // with iterators.\n    pub fn transformer(input: Vec<(String, Command)>) -> Vec<String> {\n        let mut output = Vec::new();\n\n        for (string, command) in input {\n            // Create the new string.\n            let new_string = match command {\n                Command::Uppercase => string.to_uppercase(),\n                Command::Trim => string.trim().to_string(),\n                Command::Append(n) => string + &\"bar\".repeat(n),\n            };\n\n            // Push the new string to the output vector.\n            output.push(new_string);\n        }\n\n        output\n    }\n\n    // Equivalent to `transform` but uses an iterator instead of a loop for\n    // comparison. Don't worry, we will practice iterators later ;)\n    pub fn transformer_iter(input: Vec<(String, Command)>) -> Vec<String> {\n        input\n            .into_iter()\n            .map(|(string, command)| match command {\n                Command::Uppercase => string.to_uppercase(),\n                Command::Trim => string.trim().to_string(),\n                Command::Append(n) => string + &\"bar\".repeat(n),\n            })\n            .collect()\n    }\n}\n\nfn main() {\n    // You can optionally experiment here.\n}\n\n#[cfg(test)]\nmod tests {\n    // Import `transformer`.\n    use super::my_module::transformer;\n\n    use super::Command;\n    use super::my_module::transformer_iter;\n\n    #[test]\n    fn it_works() {\n        for transformer in [transformer, transformer_iter] {\n            let input = vec![\n                (\"hello\".to_string(), Command::Uppercase),\n                (\" all roads lead to rome! \".to_string(), Command::Trim),\n                (\"foo\".to_string(), Command::Append(1)),\n                (\"bar\".to_string(), Command::Append(5)),\n            ];\n            let output = transformer(input);\n\n            assert_eq!(\n                output,\n                [\n                    \"HELLO\",\n                    \"all roads lead to rome!\",\n                    \"foobar\",\n                    \"barbarbarbarbarbar\",\n                ]\n            );\n        }\n    }\n}\n"
  },
  {
    "path": "solutions/quizzes/quiz3.rs",
    "content": "// An imaginary magical school has a new report card generation system written\n// in Rust! Currently, the system only supports creating report cards where the\n// student's grade is represented numerically (e.g. 1.0 -> 5.5). However, the\n// school also issues alphabetical grades (A+ -> F-) and needs to be able to\n// print both types of report card!\n//\n// Make the necessary code changes in the struct `ReportCard` and the impl\n// block to support alphabetical report cards in addition to numerical ones.\n\nuse std::fmt::Display;\n\n// Make the struct generic over `T`.\nstruct ReportCard<T> {\n    //           ^^^\n    grade: T,\n    //     ^\n    student_name: String,\n    student_age: u8,\n}\n\n// To be able to print the grade, it has to implement the `Display` trait.\nimpl<T: Display> ReportCard<T> {\n    //  ^^^^^^^ require that `T` implements `Display`.\n    fn print(&self) -> String {\n        format!(\n            \"{} ({}) - achieved a grade of {}\",\n            &self.student_name, &self.student_age, &self.grade,\n        )\n    }\n}\n\nfn main() {\n    // You can optionally experiment here.\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    #[test]\n    fn generate_numeric_report_card() {\n        let report_card = ReportCard {\n            grade: 2.1,\n            student_name: \"Tom Wriggle\".to_string(),\n            student_age: 12,\n        };\n        assert_eq!(\n            report_card.print(),\n            \"Tom Wriggle (12) - achieved a grade of 2.1\",\n        );\n    }\n\n    #[test]\n    fn generate_alphabetic_report_card() {\n        let report_card = ReportCard {\n            grade: \"A+\",\n            student_name: \"Gary Plotter\".to_string(),\n            student_age: 11,\n        };\n        assert_eq!(\n            report_card.print(),\n            \"Gary Plotter (11) - achieved a grade of A+\",\n        );\n    }\n}\n"
  },
  {
    "path": "src/app_state.rs",
    "content": "use anyhow::{Context, Error, Result, bail};\nuse crossterm::{QueueableCommand, cursor, terminal};\nuse std::{\n    collections::HashSet,\n    env,\n    fs::{File, OpenOptions},\n    io::{Read, Seek, StdoutLock, Write},\n    path::{MAIN_SEPARATOR_STR, Path},\n    process::{Command, Stdio},\n    sync::{\n        atomic::{AtomicUsize, Ordering::Relaxed},\n        mpsc,\n    },\n    thread,\n};\n\nuse crate::{\n    clear_terminal,\n    cmd::CmdRunner,\n    embedded::EMBEDDED_FILES,\n    exercise::{Exercise, RunnableExercise},\n    info_file::ExerciseInfo,\n    term::{self, CheckProgressVisualizer},\n};\n\nconst STATE_FILE_NAME: &str = \".rustlings-state.txt\";\nconst DEFAULT_CHECK_PARALLELISM: usize = 8;\n\n#[must_use]\npub enum ExercisesProgress {\n    // All exercises are done.\n    AllDone,\n    // A new exercise is now pending.\n    NewPending,\n    // The current exercise is still pending.\n    CurrentPending,\n}\n\npub enum StateFileStatus {\n    Read,\n    NotRead,\n}\n\n#[derive(Clone, Copy)]\npub enum CheckProgress {\n    None,\n    Checking,\n    Done,\n    Pending,\n}\n\npub struct AppState {\n    current_exercise_ind: usize,\n    exercises: Vec<Exercise>,\n    // Cache the number of done exercises to avoid iterating over all exercises every time.\n    n_done: u32,\n    final_message: &'static str,\n    state_file: File,\n    // Preallocated buffer for reading and writing the state file.\n    file_buf: Vec<u8>,\n    official_exercises: bool,\n    cmd_runner: CmdRunner,\n    emit_file_links: bool,\n}\n\nimpl AppState {\n    pub fn new(\n        exercise_infos: Vec<ExerciseInfo>,\n        final_message: &'static str,\n    ) -> Result<(Self, StateFileStatus)> {\n        let cmd_runner = CmdRunner::build()?;\n        let mut state_file = OpenOptions::new()\n            .create(true)\n            .read(true)\n            .write(true)\n            .truncate(false)\n            .open(STATE_FILE_NAME)\n            .with_context(|| {\n                format!(\"Failed to open or create the state file {STATE_FILE_NAME}\")\n            })?;\n\n        let dir_canonical_path = term::canonicalize(\"exercises\");\n        let mut exercises = exercise_infos\n            .into_iter()\n            .map(|exercise_info| {\n                let canonical_path = dir_canonical_path.as_deref().map(|dir_canonical_path| {\n                    let mut canonical_path;\n                    if let Some(dir) = exercise_info.dir {\n                        canonical_path = String::with_capacity(\n                            2 + dir_canonical_path.len() + dir.len() + exercise_info.name.len(),\n                        );\n                        canonical_path.push_str(dir_canonical_path);\n                        canonical_path.push_str(MAIN_SEPARATOR_STR);\n                        canonical_path.push_str(dir);\n                    } else {\n                        canonical_path = String::with_capacity(\n                            1 + dir_canonical_path.len() + exercise_info.name.len(),\n                        );\n                        canonical_path.push_str(dir_canonical_path);\n                    }\n\n                    canonical_path.push_str(MAIN_SEPARATOR_STR);\n                    canonical_path.push_str(exercise_info.name);\n                    canonical_path.push_str(\".rs\");\n                    canonical_path\n                });\n\n                Exercise {\n                    name: exercise_info.name,\n                    dir: exercise_info.dir,\n                    path: exercise_info.path(),\n                    canonical_path,\n                    test: exercise_info.test,\n                    strict_clippy: exercise_info.strict_clippy,\n                    hint: exercise_info.hint.trim_ascii(),\n                    // Updated below.\n                    done: false,\n                }\n            })\n            .collect::<Vec<_>>();\n\n        let mut current_exercise_ind = 0;\n        let mut n_done = 0;\n        let mut file_buf = Vec::with_capacity(2048);\n        let state_file_status = 'block: {\n            if state_file.read_to_end(&mut file_buf).is_err() {\n                break 'block StateFileStatus::NotRead;\n            }\n\n            // See `Self::write` for more information about the file format.\n            let mut lines = file_buf.split(|c| *c == b'\\n').skip(2);\n\n            let Some(current_exercise_name) = lines.next() else {\n                break 'block StateFileStatus::NotRead;\n            };\n\n            if current_exercise_name.is_empty() || lines.next().is_none() {\n                break 'block StateFileStatus::NotRead;\n            }\n\n            let mut done_exercises = HashSet::with_capacity(exercises.len());\n\n            for done_exercise_name in lines {\n                if done_exercise_name.is_empty() {\n                    break;\n                }\n                done_exercises.insert(done_exercise_name);\n            }\n\n            for (ind, exercise) in exercises.iter_mut().enumerate() {\n                if done_exercises.contains(exercise.name.as_bytes()) {\n                    exercise.done = true;\n                    n_done += 1;\n                }\n\n                if exercise.name.as_bytes() == current_exercise_name {\n                    current_exercise_ind = ind;\n                }\n            }\n\n            StateFileStatus::Read\n        };\n\n        file_buf.clear();\n        file_buf.extend_from_slice(STATE_FILE_HEADER);\n\n        let slf = Self {\n            current_exercise_ind,\n            exercises,\n            n_done,\n            final_message,\n            state_file,\n            file_buf,\n            official_exercises: !Path::new(\"info.toml\").exists(),\n            cmd_runner,\n            // VS Code has its own file link handling\n            emit_file_links: env::var_os(\"TERM_PROGRAM\").is_none_or(|v| v != \"vscode\"),\n        };\n\n        Ok((slf, state_file_status))\n    }\n\n    #[inline]\n    pub fn current_exercise_ind(&self) -> usize {\n        self.current_exercise_ind\n    }\n\n    #[inline]\n    pub fn exercises(&self) -> &[Exercise] {\n        &self.exercises\n    }\n\n    #[inline]\n    pub fn n_done(&self) -> u32 {\n        self.n_done\n    }\n\n    #[inline]\n    pub fn n_pending(&self) -> u32 {\n        self.exercises.len() as u32 - self.n_done\n    }\n\n    #[inline]\n    pub fn current_exercise(&self) -> &Exercise {\n        &self.exercises[self.current_exercise_ind]\n    }\n\n    #[inline]\n    pub fn cmd_runner(&self) -> &CmdRunner {\n        &self.cmd_runner\n    }\n\n    #[inline]\n    pub fn emit_file_links(&self) -> bool {\n        self.emit_file_links\n    }\n\n    // Write the state file.\n    // The file's format is very simple:\n    // - The first line is a comment.\n    // - The second line is an empty line.\n    // - The third line is the name of the current exercise. It must end with `\\n` even if there\n    // are no done exercises.\n    // - The fourth line is an empty line.\n    // - All remaining lines are the names of done exercises.\n    fn write(&mut self) -> Result<()> {\n        self.file_buf.truncate(STATE_FILE_HEADER.len());\n\n        self.file_buf\n            .extend_from_slice(self.current_exercise().name.as_bytes());\n        self.file_buf.push(b'\\n');\n\n        for exercise in &self.exercises {\n            if exercise.done {\n                self.file_buf.push(b'\\n');\n                self.file_buf.extend_from_slice(exercise.name.as_bytes());\n            }\n        }\n\n        self.state_file\n            .rewind()\n            .with_context(|| format!(\"Failed to rewind the state file {STATE_FILE_NAME}\"))?;\n        self.state_file\n            .set_len(0)\n            .with_context(|| format!(\"Failed to truncate the state file {STATE_FILE_NAME}\"))?;\n        self.state_file\n            .write_all(&self.file_buf)\n            .with_context(|| format!(\"Failed to write the state file {STATE_FILE_NAME}\"))?;\n\n        Ok(())\n    }\n\n    pub fn set_current_exercise_ind(&mut self, exercise_ind: usize) -> Result<()> {\n        if exercise_ind == self.current_exercise_ind {\n            return Ok(());\n        }\n\n        if exercise_ind >= self.exercises.len() {\n            bail!(BAD_INDEX_ERR);\n        }\n\n        self.current_exercise_ind = exercise_ind;\n\n        self.write()\n    }\n\n    pub fn set_current_exercise_by_name(&mut self, name: &str) -> Result<()> {\n        // O(N) is fine since this method is used only once until the program exits.\n        // Building a hashmap would have more overhead.\n        self.current_exercise_ind = self\n            .exercises\n            .iter()\n            .position(|exercise| exercise.name == name)\n            .with_context(|| format!(\"No exercise found for '{name}'!\"))?;\n\n        self.write()\n    }\n\n    // Set the status of an exercise without saving. Returns `true` if the\n    // status actually changed (and thus needs saving later).\n    pub fn set_status(&mut self, exercise_ind: usize, done: bool) -> Result<bool> {\n        let exercise = self\n            .exercises\n            .get_mut(exercise_ind)\n            .context(BAD_INDEX_ERR)?;\n\n        if exercise.done == done {\n            return Ok(false);\n        }\n\n        exercise.done = done;\n        if done {\n            self.n_done += 1;\n        } else {\n            self.n_done -= 1;\n        }\n\n        Ok(true)\n    }\n\n    // Set the status of an exercise to \"pending\" and save.\n    pub fn set_pending(&mut self, exercise_ind: usize) -> Result<()> {\n        if self.set_status(exercise_ind, false)? {\n            self.write()?;\n        }\n\n        Ok(())\n    }\n\n    // Official exercises: Dump the original file from the binary.\n    // Community exercises: Reset the exercise file with `git stash`.\n    fn reset(&self, exercise_ind: usize, path: &str) -> Result<()> {\n        if self.official_exercises {\n            return EMBEDDED_FILES\n                .write_exercise_to_disk(exercise_ind, path)\n                .with_context(|| format!(\"Failed to reset the exercise {path}\"));\n        }\n\n        let output = Command::new(\"git\")\n            .arg(\"stash\")\n            .arg(\"push\")\n            .arg(\"--\")\n            .arg(path)\n            .stdin(Stdio::null())\n            .stdout(Stdio::null())\n            .output()\n            .with_context(|| format!(\"Failed to run `git stash push -- {path}`\"))?;\n\n        if !output.status.success() {\n            bail!(\n                \"`git stash push -- {path}` didn't run successfully: {}\",\n                String::from_utf8_lossy(&output.stderr),\n            );\n        }\n\n        Ok(())\n    }\n\n    pub fn reset_current_exercise(&mut self) -> Result<&str> {\n        self.set_pending(self.current_exercise_ind)?;\n        let exercise = self.current_exercise();\n        self.reset(self.current_exercise_ind, &exercise.path)?;\n\n        Ok(&exercise.path)\n    }\n\n    // Reset the exercise by index and return its name.\n    pub fn reset_exercise_by_ind(&mut self, exercise_ind: usize) -> Result<&'static str> {\n        if exercise_ind >= self.exercises.len() {\n            bail!(BAD_INDEX_ERR);\n        }\n\n        self.set_pending(exercise_ind)?;\n        let exercise = &self.exercises[exercise_ind];\n        self.reset(exercise_ind, &exercise.path)?;\n\n        Ok(exercise.name)\n    }\n\n    // Return the index of the next pending exercise or `None` if all exercises are done.\n    fn next_pending_exercise_ind(&self) -> Option<usize> {\n        let next_ind = self.current_exercise_ind + 1;\n        self.exercises\n            // If the exercise done isn't the last, search for pending exercises after it.\n            .get(next_ind..)\n            .and_then(|later_exercises| {\n                later_exercises\n                    .iter()\n                    .position(|exercise| !exercise.done)\n                    .map(|ind| next_ind + ind)\n            })\n            // Search from the start.\n            .or_else(|| {\n                self.exercises[..self.current_exercise_ind]\n                    .iter()\n                    .position(|exercise| !exercise.done)\n            })\n    }\n\n    /// Official exercises: Dump the solution file from the binary and return its path.\n    /// Community exercises: Check if a solution file exists and return its path in that case.\n    pub fn current_solution_path(&self) -> Result<Option<String>> {\n        if cfg!(debug_assertions) {\n            return Ok(None);\n        }\n\n        let current_exercise = self.current_exercise();\n\n        if self.official_exercises {\n            EMBEDDED_FILES\n                .write_solution_to_disk(self.current_exercise_ind, current_exercise.name)\n                .map(Some)\n        } else {\n            let sol_path = current_exercise.sol_path();\n\n            if Path::new(&sol_path).exists() {\n                return Ok(Some(sol_path));\n            }\n\n            Ok(None)\n        }\n    }\n\n    fn check_all_exercises_impl(&mut self, stdout: &mut StdoutLock) -> Result<Option<usize>> {\n        let term_width = terminal::size()\n            .context(\"Failed to get the terminal size\")?\n            .0;\n        let mut progress_visualizer = CheckProgressVisualizer::build(stdout, term_width)?;\n\n        let next_exercise_ind = AtomicUsize::new(0);\n        let mut progresses = vec![CheckProgress::None; self.exercises.len()];\n\n        thread::scope(|s| {\n            let (exercise_progress_sender, exercise_progress_receiver) = mpsc::channel();\n            let n_threads = thread::available_parallelism()\n                .map_or(DEFAULT_CHECK_PARALLELISM, |count| count.get());\n\n            for _ in 0..n_threads {\n                let exercise_progress_sender = exercise_progress_sender.clone();\n                let next_exercise_ind = &next_exercise_ind;\n                let slf = &self;\n                thread::Builder::new()\n                    .spawn_scoped(s, move || {\n                        loop {\n                            let exercise_ind = next_exercise_ind.fetch_add(1, Relaxed);\n                            let Some(exercise) = slf.exercises.get(exercise_ind) else {\n                                // No more exercises.\n                                break;\n                            };\n\n                            if exercise_progress_sender\n                                .send((exercise_ind, CheckProgress::Checking))\n                                .is_err()\n                            {\n                                break;\n                            }\n\n                            let success = exercise.run_exercise(None, &slf.cmd_runner);\n                            let progress = match success {\n                                Ok(true) => CheckProgress::Done,\n                                Ok(false) => CheckProgress::Pending,\n                                Err(_) => CheckProgress::None,\n                            };\n\n                            if exercise_progress_sender\n                                .send((exercise_ind, progress))\n                                .is_err()\n                            {\n                                break;\n                            }\n                        }\n                    })\n                    .context(\"Failed to spawn a thread to check all exercises\")?;\n            }\n\n            // Drop this sender to detect when the last thread is done.\n            drop(exercise_progress_sender);\n\n            while let Ok((exercise_ind, progress)) = exercise_progress_receiver.recv() {\n                progresses[exercise_ind] = progress;\n                progress_visualizer.update(&progresses)?;\n            }\n\n            Ok::<_, Error>(())\n        })?;\n\n        let mut first_pending_exercise_ind = None;\n        for exercise_ind in 0..progresses.len() {\n            match progresses[exercise_ind] {\n                CheckProgress::Done => {\n                    self.set_status(exercise_ind, true)?;\n                }\n                CheckProgress::Pending => {\n                    self.set_status(exercise_ind, false)?;\n                    if first_pending_exercise_ind.is_none() {\n                        first_pending_exercise_ind = Some(exercise_ind);\n                    }\n                }\n                CheckProgress::None | CheckProgress::Checking => {\n                    // If we got an error while checking all exercises in parallel,\n                    // it could be because we exceeded the limit of open file descriptors.\n                    // Therefore, try running exercises with errors sequentially.\n                    progresses[exercise_ind] = CheckProgress::Checking;\n                    progress_visualizer.update(&progresses)?;\n\n                    let exercise = &self.exercises[exercise_ind];\n                    let success = exercise.run_exercise(None, &self.cmd_runner)?;\n                    if success {\n                        progresses[exercise_ind] = CheckProgress::Done;\n                    } else {\n                        progresses[exercise_ind] = CheckProgress::Pending;\n                        if first_pending_exercise_ind.is_none() {\n                            first_pending_exercise_ind = Some(exercise_ind);\n                        }\n                    }\n                    self.set_status(exercise_ind, success)?;\n                    progress_visualizer.update(&progresses)?;\n                }\n            }\n        }\n\n        self.write()?;\n\n        Ok(first_pending_exercise_ind)\n    }\n\n    // Return the exercise index of the first pending exercise found.\n    pub fn check_all_exercises(&mut self, stdout: &mut StdoutLock) -> Result<Option<usize>> {\n        stdout.queue(cursor::Hide)?;\n        let res = self.check_all_exercises_impl(stdout);\n        stdout.queue(cursor::Show)?;\n\n        res\n    }\n\n    /// Mark the current exercise as done and move on to the next pending exercise if one exists.\n    /// If all exercises are marked as done, run all of them to make sure that they are actually\n    /// done. If an exercise which is marked as done fails, mark it as pending and continue on it.\n    pub fn done_current_exercise<const CLEAR_BEFORE_FINAL_CHECK: bool>(\n        &mut self,\n        stdout: &mut StdoutLock,\n    ) -> Result<ExercisesProgress> {\n        let exercise = &mut self.exercises[self.current_exercise_ind];\n        if !exercise.done {\n            exercise.done = true;\n            self.n_done += 1;\n        }\n\n        if let Some(ind) = self.next_pending_exercise_ind() {\n            self.set_current_exercise_ind(ind)?;\n            return Ok(ExercisesProgress::NewPending);\n        }\n\n        if CLEAR_BEFORE_FINAL_CHECK {\n            clear_terminal(stdout)?;\n        } else {\n            stdout.write_all(b\"\\n\")?;\n        }\n\n        if let Some(first_pending_exercise_ind) = self.check_all_exercises(stdout)? {\n            self.set_current_exercise_ind(first_pending_exercise_ind)?;\n\n            return Ok(ExercisesProgress::NewPending);\n        }\n\n        self.render_final_message(stdout)?;\n\n        Ok(ExercisesProgress::AllDone)\n    }\n\n    pub fn render_final_message(&self, stdout: &mut StdoutLock) -> Result<()> {\n        clear_terminal(stdout)?;\n        stdout.write_all(FENISH_LINE.as_bytes())?;\n\n        let final_message = self.final_message.trim_ascii();\n        if !final_message.is_empty() {\n            stdout.write_all(final_message.as_bytes())?;\n            stdout.write_all(b\"\\n\")?;\n        }\n\n        Ok(())\n    }\n}\n\nconst BAD_INDEX_ERR: &str = \"The current exercise index is higher than the number of exercises\";\nconst STATE_FILE_HEADER: &[u8] = b\"DON'T EDIT THIS FILE!\\n\\n\";\nconst FENISH_LINE: &str = \"+----------------------------------------------------+\n|          You made it to the Fe-nish line!          |\n+--------------------------  ------------------------+\n                           \\\\/\\x1b[31m\n     ▒▒          ▒▒▒▒▒▒▒▒      ▒▒▒▒▒▒▒▒          ▒▒\n   ▒▒▒▒  ▒▒    ▒▒        ▒▒  ▒▒        ▒▒    ▒▒  ▒▒▒▒\n   ▒▒▒▒  ▒▒  ▒▒            ▒▒            ▒▒  ▒▒  ▒▒▒▒\n ░░▒▒▒▒░░▒▒  ▒▒            ▒▒            ▒▒  ▒▒░░▒▒▒▒\n   ▓▓▓▓▓▓▓▓  ▓▓      ▓▓██  ▓▓  ▓▓██      ▓▓  ▓▓▓▓▓▓▓▓\n     ▒▒▒▒    ▒▒      ████  ▒▒  ████      ▒▒░░  ▒▒▒▒\n       ▒▒  ▒▒▒▒▒▒        ▒▒▒▒▒▒        ▒▒▒▒▒▒  ▒▒\n         ▒▒▒▒▒▒▒▒▒▒▓▓▓▓▓▓▒▒▒▒▒▒▒▒▓▓▓▓▓▓▒▒▒▒▒▒▒▒\n           ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒\n             ▒▒▒▒▒▒▒▒▒▒██▒▒▒▒▒▒██▒▒▒▒▒▒▒▒▒▒\n           ▒▒  ▒▒▒▒▒▒▒▒▒▒██████▒▒▒▒▒▒▒▒▒▒  ▒▒\n         ▒▒    ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒    ▒▒\n       ▒▒    ▒▒    ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒    ▒▒    ▒▒\n       ▒▒  ▒▒    ▒▒                  ▒▒    ▒▒  ▒▒\n           ▒▒  ▒▒                      ▒▒  ▒▒\\x1b[0m\n\n\";\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    fn dummy_exercise() -> Exercise {\n        Exercise {\n            name: \"0\",\n            dir: None,\n            path: String::from(\"exercises/0.rs\"),\n            canonical_path: None,\n            test: false,\n            strict_clippy: false,\n            hint: \"\",\n            done: false,\n        }\n    }\n\n    #[test]\n    fn next_pending_exercise() {\n        let mut app_state = AppState {\n            current_exercise_ind: 0,\n            exercises: vec![dummy_exercise(), dummy_exercise(), dummy_exercise()],\n            n_done: 0,\n            final_message: \"\",\n            state_file: tempfile::tempfile().unwrap(),\n            file_buf: Vec::new(),\n            official_exercises: true,\n            cmd_runner: CmdRunner::build().unwrap(),\n            emit_file_links: true,\n        };\n\n        let mut assert = |done: [bool; 3], expected: [Option<usize>; 3]| {\n            for (exercise, done) in app_state.exercises.iter_mut().zip(done) {\n                exercise.done = done;\n            }\n            for (ind, expected) in expected.into_iter().enumerate() {\n                app_state.current_exercise_ind = ind;\n                assert_eq!(\n                    app_state.next_pending_exercise_ind(),\n                    expected,\n                    \"done={done:?}, ind={ind}\",\n                );\n            }\n        };\n\n        assert([true, true, true], [None, None, None]);\n        assert([false, false, false], [Some(1), Some(2), Some(0)]);\n        assert([false, true, true], [None, Some(0), Some(0)]);\n        assert([true, false, true], [Some(1), None, Some(1)]);\n        assert([true, true, false], [Some(2), Some(2), None]);\n        assert([true, false, false], [Some(1), Some(2), Some(1)]);\n        assert([false, true, false], [Some(2), Some(2), Some(0)]);\n        assert([false, false, true], [Some(1), Some(0), Some(0)]);\n    }\n}\n"
  },
  {
    "path": "src/cargo_toml.rs",
    "content": "use anyhow::{Context, Result};\nuse std::path::Path;\n\nuse crate::{exercise::RunnableExercise, info_file::ExerciseInfo};\n\n/// Initial capacity of the bins buffer.\npub const BINS_BUFFER_CAPACITY: usize = 1 << 14;\n\n/// Return the start and end index of the content of the list `bin = […]`.\n/// bin = [xxxxxxxxxxxxxxxxx]\n///        |start_ind       |\n///                         |end_ind\npub fn bins_start_end_ind(cargo_toml: &str) -> Result<(usize, usize)> {\n    let start_ind = cargo_toml\n        .find(\"bin = [\")\n        .context(\"Failed to find the start of the `bin` list (`bin = [`)\")?\n        + 7;\n    let end_ind = start_ind\n        + cargo_toml\n            .get(start_ind..)\n            .and_then(|slice| slice.as_bytes().iter().position(|c| *c == b']'))\n            .context(\"Failed to find the end of the `bin` list (`]`)\")?;\n\n    Ok((start_ind, end_ind))\n}\n\n/// Generate and append the content of the `bin` list in `Cargo.toml`.\n/// The `exercise_path_prefix` is the prefix of the `path` field of every list entry.\npub fn append_bins(\n    buf: &mut Vec<u8>,\n    exercise_infos: &[ExerciseInfo],\n    exercise_path_prefix: &[u8],\n) {\n    buf.push(b'\\n');\n    for exercise_info in exercise_infos {\n        buf.extend_from_slice(b\"  { name = \\\"\");\n        buf.extend_from_slice(exercise_info.name.as_bytes());\n        buf.extend_from_slice(b\"\\\", path = \\\"\");\n        buf.extend_from_slice(exercise_path_prefix);\n        buf.extend_from_slice(b\"exercises/\");\n        if let Some(dir) = exercise_info.dir {\n            buf.extend_from_slice(dir.as_bytes());\n            buf.push(b'/');\n        }\n        buf.extend_from_slice(exercise_info.name.as_bytes());\n        buf.extend_from_slice(b\".rs\\\" },\\n\");\n\n        let sol_path = exercise_info.sol_path();\n        if !Path::new(&sol_path).exists() {\n            continue;\n        }\n\n        buf.extend_from_slice(b\"  { name = \\\"\");\n        buf.extend_from_slice(exercise_info.name.as_bytes());\n        buf.extend_from_slice(b\"_sol\");\n        buf.extend_from_slice(b\"\\\", path = \\\"\");\n        buf.extend_from_slice(exercise_path_prefix);\n        buf.extend_from_slice(b\"solutions/\");\n        if let Some(dir) = exercise_info.dir {\n            buf.extend_from_slice(dir.as_bytes());\n            buf.push(b'/');\n        }\n        buf.extend_from_slice(exercise_info.name.as_bytes());\n        buf.extend_from_slice(b\".rs\\\" },\\n\");\n    }\n}\n\n/// Update the `bin` list and leave everything else unchanged.\npub fn updated_cargo_toml(\n    exercise_infos: &[ExerciseInfo],\n    current_cargo_toml: &str,\n    exercise_path_prefix: &[u8],\n) -> Result<Vec<u8>> {\n    let (bins_start_ind, bins_end_ind) = bins_start_end_ind(current_cargo_toml)?;\n\n    let mut updated_cargo_toml = Vec::with_capacity(BINS_BUFFER_CAPACITY);\n    updated_cargo_toml.extend_from_slice(&current_cargo_toml.as_bytes()[..bins_start_ind]);\n    append_bins(\n        &mut updated_cargo_toml,\n        exercise_infos,\n        exercise_path_prefix,\n    );\n    updated_cargo_toml.extend_from_slice(&current_cargo_toml.as_bytes()[bins_end_ind..]);\n\n    Ok(updated_cargo_toml)\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    #[test]\n    fn test_bins_start_end_ind() {\n        assert_eq!(bins_start_end_ind(\"\").ok(), None);\n        assert_eq!(bins_start_end_ind(\"[]\").ok(), None);\n        assert_eq!(bins_start_end_ind(\"bin = [\").ok(), None);\n        assert_eq!(bins_start_end_ind(\"bin = ]\").ok(), None);\n        assert_eq!(bins_start_end_ind(\"bin = []\").ok(), Some((7, 7)));\n        assert_eq!(bins_start_end_ind(\"bin= []\").ok(), None);\n        assert_eq!(bins_start_end_ind(\"bin =[]\").ok(), None);\n        assert_eq!(bins_start_end_ind(\"bin=[]\").ok(), None);\n        assert_eq!(bins_start_end_ind(\"bin = [\\nxxx\\n]\").ok(), Some((7, 12)));\n    }\n\n    #[test]\n    fn test_bins() {\n        let exercise_infos = [\n            ExerciseInfo {\n                name: \"1\",\n                dir: None,\n                test: true,\n                strict_clippy: true,\n                hint: \"\",\n                skip_check_unsolved: false,\n            },\n            ExerciseInfo {\n                name: \"2\",\n                dir: Some(\"d\"),\n                test: false,\n                strict_clippy: false,\n                hint: \"\",\n                skip_check_unsolved: false,\n            },\n        ];\n\n        let mut buf = Vec::with_capacity(128);\n        append_bins(&mut buf, &exercise_infos, b\"\");\n        assert_eq!(\n            buf,\n            br#\"\n  { name = \"1\", path = \"exercises/1.rs\" },\n  { name = \"2\", path = \"exercises/d/2.rs\" },\n\"#,\n        );\n\n        assert_eq!(\n            updated_cargo_toml(\n                &exercise_infos,\n                \"abc\\n\\\n                 bin = [xxx]\\n\\\n                 123\",\n                b\"../\"\n            )\n            .unwrap(),\n            br#\"abc\nbin = [\n  { name = \"1\", path = \"../exercises/1.rs\" },\n  { name = \"2\", path = \"../exercises/d/2.rs\" },\n]\n123\"#,\n        );\n    }\n}\n"
  },
  {
    "path": "src/cmd.rs",
    "content": "use anyhow::{Context, Result, bail};\nuse serde::Deserialize;\nuse std::{\n    io::{Read, pipe},\n    path::PathBuf,\n    process::{Command, Stdio},\n};\n\n/// Run a command with a description for a possible error and append the merged stdout and stderr.\n/// The boolean in the returned `Result` is true if the command's exit status is success.\nfn run_cmd(mut cmd: Command, description: &str, output: Option<&mut Vec<u8>>) -> Result<bool> {\n    let spawn = |mut cmd: Command| {\n        // NOTE: The closure drops `cmd` which prevents a pipe deadlock.\n        cmd.stdin(Stdio::null())\n            .spawn()\n            .with_context(|| format!(\"Failed to run the command `{description}`\"))\n    };\n\n    let mut handle = if let Some(output) = output {\n        let (mut reader, writer) = pipe().with_context(|| {\n            format!(\"Failed to create a pipe to run the command `{description}``\")\n        })?;\n\n        let writer_clone = writer.try_clone().with_context(|| {\n            format!(\"Failed to clone the pipe writer for the command `{description}`\")\n        })?;\n\n        cmd.stdout(writer_clone).stderr(writer);\n        let handle = spawn(cmd)?;\n\n        reader\n            .read_to_end(output)\n            .with_context(|| format!(\"Failed to read the output of the command `{description}`\"))?;\n\n        output.push(b'\\n');\n\n        handle\n    } else {\n        cmd.stdout(Stdio::null()).stderr(Stdio::null());\n        spawn(cmd)?\n    };\n\n    handle\n        .wait()\n        .with_context(|| format!(\"Failed to wait on the command `{description}` to exit\"))\n        .map(|status| status.success())\n}\n\n// Parses parts of the output of `cargo metadata`.\n#[derive(Deserialize)]\nstruct CargoMetadata {\n    target_directory: PathBuf,\n}\n\npub struct CmdRunner {\n    target_dir: PathBuf,\n}\n\nimpl CmdRunner {\n    pub fn build() -> Result<Self> {\n        // Get the target directory from Cargo.\n        let metadata_output = Command::new(\"cargo\")\n            .arg(\"metadata\")\n            .arg(\"-q\")\n            .arg(\"--format-version\")\n            .arg(\"1\")\n            .arg(\"--no-deps\")\n            .stdin(Stdio::null())\n            .stderr(Stdio::inherit())\n            .output()\n            .context(CARGO_METADATA_ERR)?;\n\n        if !metadata_output.status.success() {\n            bail!(\"The command `cargo metadata …` failed. Are you in the `rustlings/` directory?\");\n        }\n\n        let metadata: CargoMetadata = serde_json::de::from_slice(&metadata_output.stdout)\n            .context(\n                \"Failed to read the field `target_directory` from the output of the command `cargo metadata …`\",\n            )?;\n\n        Ok(Self {\n            target_dir: metadata.target_directory,\n        })\n    }\n\n    pub fn cargo<'out>(\n        &self,\n        subcommand: &str,\n        bin_name: &str,\n        output: Option<&'out mut Vec<u8>>,\n    ) -> CargoSubcommand<'out> {\n        let mut cmd = Command::new(\"cargo\");\n        cmd.arg(subcommand).arg(\"-q\").arg(\"--bin\").arg(bin_name);\n\n        // A hack to make `cargo run` work when developing Rustlings.\n        #[cfg(debug_assertions)]\n        cmd.arg(\"--manifest-path\")\n            .arg(\"dev/Cargo.toml\")\n            .arg(\"--target-dir\")\n            .arg(&self.target_dir);\n\n        if output.is_some() {\n            cmd.arg(\"--color\").arg(\"always\");\n        }\n\n        CargoSubcommand { cmd, output }\n    }\n\n    /// The boolean in the returned `Result` is true if the command's exit status is success.\n    pub fn run_debug_bin(&self, bin_name: &str, output: Option<&mut Vec<u8>>) -> Result<bool> {\n        // 7 = \"/debug/\".len()\n        let mut bin_path =\n            PathBuf::with_capacity(self.target_dir.as_os_str().len() + 7 + bin_name.len());\n        bin_path.push(&self.target_dir);\n        bin_path.push(\"debug\");\n        bin_path.push(bin_name);\n\n        run_cmd(Command::new(&bin_path), &bin_path.to_string_lossy(), output)\n    }\n}\n\npub struct CargoSubcommand<'out> {\n    cmd: Command,\n    output: Option<&'out mut Vec<u8>>,\n}\n\nimpl CargoSubcommand<'_> {\n    #[inline]\n    pub fn args<'arg, I>(&mut self, args: I) -> &mut Self\n    where\n        I: IntoIterator<Item = &'arg str>,\n    {\n        self.cmd.args(args);\n        self\n    }\n\n    /// The boolean in the returned `Result` is true if the command's exit status is success.\n    #[inline]\n    pub fn run(self, description: &str) -> Result<bool> {\n        run_cmd(self.cmd, description, self.output)\n    }\n}\n\nconst CARGO_METADATA_ERR: &str = \"Failed to run the command `cargo metadata …`\nDid you already install Rust?\nTry running `cargo --version` to diagnose the problem.\";\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    #[test]\n    fn test_run_cmd() {\n        let mut cmd = Command::new(\"echo\");\n        cmd.arg(\"Hello\");\n\n        let mut output = Vec::with_capacity(8);\n        run_cmd(cmd, \"echo …\", Some(&mut output)).unwrap();\n\n        assert_eq!(output, b\"Hello\\n\\n\");\n    }\n}\n"
  },
  {
    "path": "src/dev/check.rs",
    "content": "use anyhow::{Context, Error, Result, anyhow, bail};\nuse std::{\n    cmp::Ordering,\n    collections::HashSet,\n    fs::{self, OpenOptions, read_dir},\n    io::{self, Read, Write},\n    path::{Path, PathBuf},\n    process::{Command, Stdio},\n    thread,\n};\n\nuse crate::{\n    CURRENT_FORMAT_VERSION,\n    cargo_toml::{BINS_BUFFER_CAPACITY, append_bins, bins_start_end_ind},\n    cmd::CmdRunner,\n    exercise::{OUTPUT_CAPACITY, RunnableExercise},\n    info_file::{ExerciseInfo, InfoFile},\n    term::ProgressCounter,\n};\n\nconst MAX_N_EXERCISES: usize = 999;\nconst MAX_EXERCISE_NAME_LEN: usize = 32;\n\n// Find a char that isn't allowed in the exercise's `name` or `dir`.\nfn forbidden_char(input: &str) -> Option<char> {\n    input.chars().find(|c| !c.is_alphanumeric() && *c != '_')\n}\n\n// Check that the `Cargo.toml` file is up-to-date.\nfn check_cargo_toml(\n    exercise_infos: &[ExerciseInfo],\n    cargo_toml_path: &str,\n    exercise_path_prefix: &[u8],\n) -> Result<()> {\n    let current_cargo_toml = fs::read_to_string(cargo_toml_path)\n        .with_context(|| format!(\"Failed to read the file `{cargo_toml_path}`\"))?;\n\n    let (bins_start_ind, bins_end_ind) = bins_start_end_ind(&current_cargo_toml)?;\n\n    let old_bins = &current_cargo_toml.as_bytes()[bins_start_ind..bins_end_ind];\n    let mut new_bins = Vec::with_capacity(BINS_BUFFER_CAPACITY);\n    append_bins(&mut new_bins, exercise_infos, exercise_path_prefix);\n\n    if old_bins != new_bins {\n        if cfg!(debug_assertions) {\n            bail!(\n                \"The file `dev/Cargo.toml` is outdated. Run `cargo dev update` to update it. Then run `cargo run -- dev check` again\"\n            );\n        }\n\n        bail!(\n            \"The file `Cargo.toml` is outdated. Run `rustlings dev update` to update it. Then run `rustlings dev check` again\"\n        );\n    }\n\n    Ok(())\n}\n\n// Check the info of all exercises and return their paths in a set.\nfn check_info_file_exercises(info_file: &InfoFile) -> Result<HashSet<PathBuf>> {\n    let mut names = HashSet::with_capacity(info_file.exercises.len());\n    let mut paths = HashSet::with_capacity(info_file.exercises.len());\n\n    let mut file_buf = String::with_capacity(1 << 14);\n    for exercise_info in &info_file.exercises {\n        let name = exercise_info.name;\n        if name.is_empty() {\n            bail!(\"Found an empty exercise name in `info.toml`\");\n        }\n        if name.len() > MAX_EXERCISE_NAME_LEN {\n            bail!(\n                \"The length of the exercise name `{name}` is bigger than the maximum {MAX_EXERCISE_NAME_LEN}\"\n            );\n        }\n        if let Some(c) = forbidden_char(name) {\n            bail!(\"Char `{c}` in the exercise name `{name}` is not allowed\");\n        }\n\n        if let Some(dir) = exercise_info.dir {\n            if dir.is_empty() {\n                bail!(\"The exercise `{name}` has an empty dir name in `info.toml`\");\n            }\n            if let Some(c) = forbidden_char(dir) {\n                bail!(\"Char `{c}` in the exercise dir `{dir}` is not allowed\");\n            }\n        }\n\n        if exercise_info.hint.trim_ascii().is_empty() {\n            bail!(\n                \"The exercise `{name}` has an empty hint. Please provide a hint or at least tell the user why a hint isn't needed for this exercise\"\n            );\n        }\n\n        if !names.insert(name) {\n            bail!(\"The exercise name `{name}` is duplicated. Exercise names must all be unique\");\n        }\n\n        let path = exercise_info.path();\n\n        OpenOptions::new()\n            .read(true)\n            .open(&path)\n            .with_context(|| format!(\"Failed to open the file {path}\"))?\n            .read_to_string(&mut file_buf)\n            .with_context(|| format!(\"Failed to read the file {path}\"))?;\n\n        if !file_buf.contains(\"fn main()\") {\n            bail!(\n                \"The `main` function is missing in the file `{path}`.\\n\\\n                 Create at least an empty `main` function to avoid language server errors\"\n            );\n        }\n\n        if !file_buf.contains(\"// TODO\") {\n            bail!(\n                \"Didn't find any `// TODO` comment in the file `{path}`.\\n\\\n                 You need to have at least one such comment to guide the user.\"\n            );\n        }\n\n        let contains_tests = file_buf.contains(\"#[test]\\n\");\n        if exercise_info.test {\n            if !contains_tests {\n                bail!(\n                    \"The file `{path}` doesn't contain any tests. If you don't want to add tests to this exercise, set `test = false` for this exercise in the `info.toml` file\"\n                );\n            }\n        } else if contains_tests {\n            bail!(\n                \"The file `{path}` contains tests annotated with `#[test]` but the exercise `{name}` has `test = false` in the `info.toml` file\"\n            );\n        }\n\n        file_buf.clear();\n\n        paths.insert(PathBuf::from(path));\n    }\n\n    Ok(paths)\n}\n\n// Check `dir` for unexpected files.\n// Only Rust files in `allowed_rust_files` and `README.md` files are allowed.\n// Only one level of directory nesting is allowed.\nfn check_unexpected_files(dir: &str, allowed_rust_files: &HashSet<PathBuf>) -> Result<()> {\n    let unexpected_file = |path: &Path| {\n        anyhow!(\n            \"Found the file `{}`. Only `README.md` and Rust files related to an exercise in `info.toml` are allowed in the `{dir}` directory\",\n            path.display()\n        )\n    };\n\n    for entry in read_dir(dir).with_context(|| format!(\"Failed to open the `{dir}` directory\"))? {\n        let entry = entry.with_context(|| format!(\"Failed to read the `{dir}` directory\"))?;\n\n        if entry.file_type().unwrap().is_file() {\n            let path = entry.path();\n            let file_name = path.file_name().unwrap();\n            if file_name == \"README.md\" {\n                continue;\n            }\n\n            if !allowed_rust_files.contains(&path) {\n                return Err(unexpected_file(&path));\n            }\n\n            continue;\n        }\n\n        let dir_path = entry.path();\n        for entry in read_dir(&dir_path)\n            .with_context(|| format!(\"Failed to open the directory {}\", dir_path.display()))?\n        {\n            let entry = entry\n                .with_context(|| format!(\"Failed to read the directory {}\", dir_path.display()))?;\n            let path = entry.path();\n\n            if !entry.file_type().unwrap().is_file() {\n                bail!(\n                    \"Found `{}` but expected only files. Only one level of exercise nesting is allowed\",\n                    path.display()\n                );\n            }\n\n            let file_name = path.file_name().unwrap();\n            if file_name == \"README.md\" {\n                continue;\n            }\n\n            if !allowed_rust_files.contains(&path) {\n                return Err(unexpected_file(&path));\n            }\n        }\n    }\n\n    Ok(())\n}\n\nfn check_exercises_unsolved(\n    info_file: &'static InfoFile,\n    cmd_runner: &'static CmdRunner,\n) -> Result<()> {\n    let mut stdout = io::stdout().lock();\n    stdout.write_all(b\"Running all exercises to check that they aren't already solved...\\n\")?;\n\n    let handles = info_file\n        .exercises\n        .iter()\n        .filter_map(|exercise_info| {\n            if exercise_info.skip_check_unsolved {\n                return None;\n            }\n\n            Some(\n                thread::Builder::new()\n                    .spawn(|| exercise_info.run_exercise(None, cmd_runner))\n                    .map(|handle| (exercise_info.name, handle)),\n            )\n        })\n        .collect::<Result<Vec<_>, _>>()\n        .context(\"Failed to spawn a thread to check if an exercise is already solved\")?;\n\n    let mut progress_counter = ProgressCounter::new(&mut stdout, handles.len())?;\n\n    for (exercise_name, handle) in handles {\n        let Ok(result) = handle.join() else {\n            bail!(\"Panic while trying to run the exercise {exercise_name}\");\n        };\n\n        match result {\n            Ok(true) => {\n                bail!(\n                    \"The exercise {exercise_name} is already solved.\\n\\\n                     {SKIP_CHECK_UNSOLVED_HINT}\",\n                )\n            }\n            Ok(false) => (),\n            Err(e) => return Err(e),\n        }\n\n        progress_counter.increment()?;\n    }\n\n    Ok(())\n}\n\nfn check_exercises(info_file: &'static InfoFile, cmd_runner: &'static CmdRunner) -> Result<()> {\n    match info_file.format_version.cmp(&CURRENT_FORMAT_VERSION) {\n        Ordering::Less => bail!(\n            \"`format_version` < {CURRENT_FORMAT_VERSION} (supported version)\\n\\\n             Please migrate to the latest format version\"\n        ),\n        Ordering::Greater => bail!(\n            \"`format_version` > {CURRENT_FORMAT_VERSION} (supported version)\\n\\\n             Try updating the Rustlings program\"\n        ),\n        Ordering::Equal => (),\n    }\n\n    let handle = thread::Builder::new()\n        .spawn(move || check_exercises_unsolved(info_file, cmd_runner))\n        .context(\"Failed to spawn a thread to check if any exercise is already solved\")?;\n\n    let info_file_paths = check_info_file_exercises(info_file)?;\n    check_unexpected_files(\"exercises\", &info_file_paths)?;\n\n    handle.join().unwrap()\n}\n\nenum SolutionCheck {\n    Success { sol_path: String },\n    MissingOptional,\n    RunFailure { output: Vec<u8> },\n    Err(Error),\n}\n\nfn check_solutions(\n    require_solutions: bool,\n    info_file: &'static InfoFile,\n    cmd_runner: &'static CmdRunner,\n) -> Result<()> {\n    let mut stdout = io::stdout().lock();\n    stdout.write_all(b\"Running all solutions...\\n\")?;\n\n    let handles = info_file\n        .exercises\n        .iter()\n        .map(|exercise_info| {\n            thread::Builder::new().spawn(move || {\n                let sol_path = exercise_info.sol_path();\n                if !Path::new(&sol_path).exists() {\n                    if require_solutions {\n                        return SolutionCheck::Err(anyhow!(\n                            \"The solution of the exercise {} is missing\",\n                            exercise_info.name,\n                        ));\n                    }\n\n                    return SolutionCheck::MissingOptional;\n                }\n\n                let mut output = Vec::with_capacity(OUTPUT_CAPACITY);\n                match exercise_info.run_solution(Some(&mut output), cmd_runner) {\n                    Ok(true) => SolutionCheck::Success { sol_path },\n                    Ok(false) => SolutionCheck::RunFailure { output },\n                    Err(e) => SolutionCheck::Err(e),\n                }\n            })\n        })\n        .collect::<Result<Vec<_>, _>>()\n        .context(\"Failed to spawn a thread to check a solution\")?;\n\n    let mut sol_paths = HashSet::with_capacity(info_file.exercises.len());\n    let mut fmt_cmd = Command::new(\"rustfmt\");\n    fmt_cmd\n        .arg(\"--check\")\n        .arg(\"--edition\")\n        .arg(\"2024\")\n        .arg(\"--color\")\n        .arg(\"always\")\n        .stdin(Stdio::null());\n\n    let mut progress_counter = ProgressCounter::new(&mut stdout, handles.len())?;\n\n    for (exercise_info, handle) in info_file.exercises.iter().zip(handles) {\n        let Ok(check_result) = handle.join() else {\n            bail!(\n                \"Panic while trying to run the solution of the exercise {}\",\n                exercise_info.name,\n            );\n        };\n\n        match check_result {\n            SolutionCheck::Success { sol_path } => {\n                fmt_cmd.arg(&sol_path);\n                sol_paths.insert(PathBuf::from(sol_path));\n            }\n            SolutionCheck::MissingOptional => (),\n            SolutionCheck::RunFailure { output } => {\n                drop(progress_counter);\n                stdout.write_all(&output)?;\n                bail!(\n                    \"Running the solution of the exercise {} failed with the error above\",\n                    exercise_info.name,\n                );\n            }\n            SolutionCheck::Err(e) => return Err(e),\n        }\n\n        progress_counter.increment()?;\n    }\n\n    let n_solutions = sol_paths.len();\n    let handle = thread::Builder::new()\n        .spawn(move || check_unexpected_files(\"solutions\", &sol_paths))\n        .context(\n            \"Failed to spawn a thread to check for unexpected files in the solutions directory\",\n        )?;\n\n    if n_solutions > 0\n        && !fmt_cmd\n            .status()\n            .context(\"Failed to run `rustfmt` on all solution files\")?\n            .success()\n    {\n        bail!(\"Some solutions aren't formatted. Run `rustfmt` on them\");\n    }\n\n    handle.join().unwrap()\n}\n\npub fn check(require_solutions: bool) -> Result<()> {\n    let info_file = InfoFile::parse()?;\n\n    if info_file.exercises.len() > MAX_N_EXERCISES {\n        bail!(\"The maximum number of exercises is {MAX_N_EXERCISES}\");\n    }\n\n    if cfg!(debug_assertions) {\n        // A hack to make `cargo dev check` work when developing Rustlings.\n        check_cargo_toml(&info_file.exercises, \"dev/Cargo.toml\", b\"../\")?;\n    } else {\n        check_cargo_toml(&info_file.exercises, \"Cargo.toml\", b\"\")?;\n    }\n\n    // Leaking is fine since they are used until the end of the program.\n    let cmd_runner = Box::leak(Box::new(CmdRunner::build()?));\n    let info_file = Box::leak(Box::new(info_file));\n\n    check_exercises(info_file, cmd_runner)?;\n    check_solutions(require_solutions, info_file, cmd_runner)?;\n\n    println!(\"Everything looks fine!\");\n\n    Ok(())\n}\n\nconst SKIP_CHECK_UNSOLVED_HINT: &str = \"If this is an introduction exercise that is intended to be already solved, add `skip_check_unsolved = true` to the exercise's metadata in the `info.toml` file\";\n"
  },
  {
    "path": "src/dev/new.rs",
    "content": "use anyhow::{Context, Result, bail};\nuse std::{\n    env::set_current_dir,\n    fs::{self, create_dir},\n    path::Path,\n    process::Command,\n};\n\nuse crate::{CURRENT_FORMAT_VERSION, init::RUST_ANALYZER_TOML};\n\n// Create a directory relative to the current directory and print its path.\nfn create_rel_dir(dir_name: &str, current_dir: &str) -> Result<()> {\n    create_dir(dir_name)\n        .with_context(|| format!(\"Failed to create the directory {current_dir}/{dir_name}\"))?;\n    println!(\"Created the directory {current_dir}/{dir_name}\");\n    Ok(())\n}\n\n// Write a file relative to the current directory and print its path.\nfn write_rel_file<C>(file_name: &str, current_dir: &str, content: C) -> Result<()>\nwhere\n    C: AsRef<[u8]>,\n{\n    fs::write(file_name, content)\n        .with_context(|| format!(\"Failed to create the file {current_dir}/{file_name}\"))?;\n    // Space to align with `create_rel_dir`.\n    println!(\"Created the file      {current_dir}/{file_name}\");\n    Ok(())\n}\n\npub fn new(path: &Path, no_git: bool) -> Result<()> {\n    let dir_path_str = path.to_string_lossy();\n\n    create_dir(path).with_context(|| format!(\"Failed to create the directory {dir_path_str}\"))?;\n    println!(\"Created the directory {dir_path_str}\");\n\n    set_current_dir(path)\n        .with_context(|| format!(\"Failed to set {dir_path_str} as the current directory\"))?;\n\n    if !no_git\n        && !Command::new(\"git\")\n            .arg(\"init\")\n            .status()\n            .context(\"Failed to run `git init`\")?\n            .success()\n    {\n        bail!(\"`git init` didn't run successfully. See the possible error message above\");\n    }\n\n    write_rel_file(\".gitignore\", &dir_path_str, GITIGNORE)?;\n\n    create_rel_dir(\"exercises\", &dir_path_str)?;\n    create_rel_dir(\"solutions\", &dir_path_str)?;\n\n    write_rel_file(\n        \"info.toml\",\n        &dir_path_str,\n        format!(\n            \"{INFO_FILE_BEFORE_FORMAT_VERSION}{CURRENT_FORMAT_VERSION}{INFO_FILE_AFTER_FORMAT_VERSION}\"\n        ),\n    )?;\n\n    write_rel_file(\"Cargo.toml\", &dir_path_str, CARGO_TOML)?;\n\n    write_rel_file(\"README.md\", &dir_path_str, README)?;\n\n    write_rel_file(\"rust-analyzer.toml\", &dir_path_str, RUST_ANALYZER_TOML)?;\n\n    create_rel_dir(\".vscode\", &dir_path_str)?;\n    write_rel_file(\n        \".vscode/extensions.json\",\n        &dir_path_str,\n        crate::init::VS_CODE_EXTENSIONS_JSON,\n    )?;\n\n    println!(\"\\nInitialization done ✓\");\n\n    Ok(())\n}\n\npub const GITIGNORE: &[u8] = b\"Cargo.lock\ntarget/\n.vscode/\n!.vscode/extensions.json\n\";\n\nconst INFO_FILE_BEFORE_FORMAT_VERSION: &str =\n    \"# The format version is an indicator of the compatibility of community exercises with the\n# Rustlings program.\n# The format version is not the same as the version of the Rustlings program.\n# In case Rustlings makes an unavoidable breaking change to the expected format of community\n# exercises, you would need to raise this version and adapt to the new format.\n# Otherwise, the newest version of the Rustlings program won't be able to run these exercises.\nformat_version = \";\n\nconst INFO_FILE_AFTER_FORMAT_VERSION: &str = r#\"\n\n# Optional multi-line message to be shown to users when just starting with the exercises.\nwelcome_message = \"\"\"Welcome to these community Rustlings exercises.\"\"\"\n\n# Optional multi-line message to be shown to users after finishing all exercises.\nfinal_message = \"\"\"We hope that you found the exercises helpful :D\"\"\"\n\n# Repeat this section for every exercise.\n[[exercises]]\n# Exercise name which is the exercise file name without the `.rs` extension.\nname = \"???\"\n\n# Optional directory name to be provided if you want to organize exercises in directories.\n# If `dir` is specified, the exercise path is `exercises/DIR/NAME.rs`\n# Otherwise, the path is `exercises/NAME.rs`\n# dir = \"???\"\n\n# Rustlings expects the exercise to contain tests and run them.\n# You can optionally disable testing by setting `test` to `false` (the default is `true`).\n# In that case, the exercise will be considered done when it just successfully compiles.\n# test = true\n\n# Rustlings will always run Clippy on exercises.\n# You can optionally set `strict_clippy` to `true` (the default is `false`) to only consider\n# the exercise as done when there are no warnings left.\n# strict_clippy = false\n\n# A multi-line hint to be shown to users on request.\nhint = \"\"\"???\"\"\"\n\"#;\n\nconst CARGO_TOML: &[u8] =\n    br#\"# Don't edit the `bin` list manually! It is updated by `rustlings dev update`\nbin = []\n\n[package]\nname = \"exercises\"\nedition = \"2024\"\n# Don't publish the exercises on crates.io!\npublish = false\n\n[dependencies]\n\"#;\n\nconst README: &str = \"# Rustlings 🦀\n\nWelcome to these community Rustlings exercises 😃\n\nFirst, [install Rustlings using the official instructions](https://github.com/rust-lang/rustlings) ✅\n\nThen, clone this repository, open a terminal in this directory and run `rustlings` to get started with the exercises 🚀\n\";\n"
  },
  {
    "path": "src/dev/update.rs",
    "content": "use anyhow::{Context, Result};\nuse std::fs;\n\nuse crate::{\n    cargo_toml::updated_cargo_toml,\n    info_file::{ExerciseInfo, InfoFile},\n};\n\n// Update the `Cargo.toml` file.\nfn update_cargo_toml(\n    exercise_infos: &[ExerciseInfo],\n    cargo_toml_path: &str,\n    exercise_path_prefix: &[u8],\n) -> Result<()> {\n    let current_cargo_toml = fs::read_to_string(cargo_toml_path)\n        .with_context(|| format!(\"Failed to read the file `{cargo_toml_path}`\"))?;\n\n    let updated_cargo_toml =\n        updated_cargo_toml(exercise_infos, &current_cargo_toml, exercise_path_prefix)?;\n\n    fs::write(cargo_toml_path, updated_cargo_toml)\n        .context(\"Failed to write the `Cargo.toml` file\")?;\n\n    Ok(())\n}\n\npub fn update() -> Result<()> {\n    let info_file = InfoFile::parse()?;\n\n    if cfg!(debug_assertions) {\n        // A hack to make `cargo dev update` work when developing Rustlings.\n        update_cargo_toml(&info_file.exercises, \"dev/Cargo.toml\", b\"../\")\n            .context(\"Failed to update the file `dev/Cargo.toml`\")?;\n\n        println!(\"Updated `dev/Cargo.toml`\");\n    } else {\n        update_cargo_toml(&info_file.exercises, \"Cargo.toml\", &[])\n            .context(\"Failed to update the file `Cargo.toml`\")?;\n\n        println!(\"Updated `Cargo.toml`\");\n    }\n\n    Ok(())\n}\n"
  },
  {
    "path": "src/dev.rs",
    "content": "use anyhow::{Context, Result, bail};\nuse clap::Subcommand;\nuse std::path::PathBuf;\n\nmod check;\nmod new;\nmod update;\n\n#[derive(Subcommand)]\npub enum DevCommands {\n    /// Create a new project for community exercises\n    New {\n        /// The path to create the project in\n        path: PathBuf,\n        /// Don't try to initialize a Git repository in the project directory\n        #[arg(long)]\n        no_git: bool,\n    },\n    /// Run checks on the exercises\n    Check {\n        /// Require that every exercise has a solution\n        #[arg(short, long)]\n        require_solutions: bool,\n    },\n    /// Update the `Cargo.toml` file for the exercises\n    Update,\n}\n\nimpl DevCommands {\n    pub fn run(self) -> Result<()> {\n        match self {\n            Self::New { path, no_git } => {\n                if cfg!(debug_assertions) {\n                    bail!(\"Disabled in the debug build\");\n                }\n\n                new::new(&path, no_git).context(INIT_ERR)\n            }\n            Self::Check { require_solutions } => check::check(require_solutions),\n            Self::Update => update::update(),\n        }\n    }\n}\n\nconst INIT_ERR: &str = \"Initialization failed.\nAfter resolving the issue, delete the `rustlings` directory (if it was created) and try again\";\n"
  },
  {
    "path": "src/embedded.rs",
    "content": "use anyhow::{Context, Error, Result};\nuse std::{\n    fs::{self, create_dir},\n    io,\n};\n\nuse crate::info_file::ExerciseInfo;\n\n/// Contains all embedded files.\npub static EMBEDDED_FILES: EmbeddedFiles = rustlings_macros::include_files!();\n\n// Files related to one exercise.\nstruct ExerciseFiles {\n    // The content of the exercise file.\n    exercise: &'static [u8],\n    // The content of the solution file.\n    solution: &'static [u8],\n    // Index of the related `ExerciseDir` in `EmbeddedFiles::exercise_dirs`.\n    dir_ind: usize,\n}\n\nfn create_dir_if_not_exists(path: &str) -> Result<()> {\n    if let Err(e) = create_dir(path)\n        && e.kind() != io::ErrorKind::AlreadyExists\n    {\n        return Err(Error::from(e).context(format!(\"Failed to create the directory {path}\")));\n    }\n\n    Ok(())\n}\n\n// A directory in the `exercises/` directory.\npub struct ExerciseDir {\n    pub name: &'static str,\n    readme: &'static [u8],\n}\n\nimpl ExerciseDir {\n    fn init_on_disk(&self) -> Result<()> {\n        // 20 = 10 + 10\n        // exercises/ + /README.md\n        let mut dir_path = String::with_capacity(20 + self.name.len());\n        dir_path.push_str(\"exercises/\");\n        dir_path.push_str(self.name);\n        create_dir_if_not_exists(&dir_path)?;\n\n        let mut readme_path = dir_path;\n        readme_path.push_str(\"/README.md\");\n\n        fs::write(&readme_path, self.readme)\n            .with_context(|| format!(\"Failed to write the file {readme_path}\"))\n    }\n}\n\n/// All embedded files.\npub struct EmbeddedFiles {\n    /// The content of the `info.toml` file.\n    pub info_file: &'static str,\n    exercise_files: &'static [ExerciseFiles],\n    pub exercise_dirs: &'static [ExerciseDir],\n}\n\nimpl EmbeddedFiles {\n    /// Dump all the embedded files of the `exercises/` directory.\n    pub fn init_exercises_dir(&self, exercise_infos: &[ExerciseInfo]) -> Result<()> {\n        create_dir(\"exercises\").context(\"Failed to create the directory `exercises`\")?;\n\n        fs::write(\n            \"exercises/README.md\",\n            include_bytes!(\"../exercises/README.md\"),\n        )\n        .context(\"Failed to write the file exercises/README.md\")?;\n\n        for dir in self.exercise_dirs {\n            dir.init_on_disk()?;\n        }\n\n        let mut exercise_path = String::with_capacity(64);\n        let prefix = \"exercises/\";\n        exercise_path.push_str(prefix);\n\n        for (exercise_info, exercise_files) in exercise_infos.iter().zip(self.exercise_files) {\n            let dir = &self.exercise_dirs[exercise_files.dir_ind];\n\n            exercise_path.truncate(prefix.len());\n            exercise_path.push_str(dir.name);\n            exercise_path.push('/');\n            exercise_path.push_str(exercise_info.name);\n            exercise_path.push_str(\".rs\");\n\n            fs::write(&exercise_path, exercise_files.exercise)\n                .with_context(|| format!(\"Failed to write the exercise file {exercise_path}\"))?;\n        }\n\n        Ok(())\n    }\n\n    pub fn write_exercise_to_disk(&self, exercise_ind: usize, path: &str) -> Result<()> {\n        let exercise_files = &self.exercise_files[exercise_ind];\n        let dir = &self.exercise_dirs[exercise_files.dir_ind];\n\n        dir.init_on_disk()?;\n        fs::write(path, exercise_files.exercise)\n            .with_context(|| format!(\"Failed to write the exercise file {path}\"))\n    }\n\n    /// Write the solution file to disk and return its path.\n    pub fn write_solution_to_disk(\n        &self,\n        exercise_ind: usize,\n        exercise_name: &str,\n    ) -> Result<String> {\n        create_dir_if_not_exists(\"solutions\")?;\n\n        let exercise_files = &self.exercise_files[exercise_ind];\n        let dir = &self.exercise_dirs[exercise_files.dir_ind];\n\n        // 14 = 10 + 1 + 3\n        // solutions/ + / + .rs\n        let mut dir_path = String::with_capacity(14 + dir.name.len() + exercise_name.len());\n        dir_path.push_str(\"solutions/\");\n        dir_path.push_str(dir.name);\n        create_dir_if_not_exists(&dir_path)?;\n\n        let mut solution_path = dir_path;\n        solution_path.push('/');\n        solution_path.push_str(exercise_name);\n        solution_path.push_str(\".rs\");\n\n        fs::write(&solution_path, exercise_files.solution)\n            .with_context(|| format!(\"Failed to write the solution file {solution_path}\"))?;\n\n        Ok(solution_path)\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use serde::Deserialize;\n\n    use super::*;\n\n    #[derive(Deserialize)]\n    struct ExerciseInfo<'a> {\n        dir: &'a str,\n    }\n\n    #[derive(Deserialize)]\n    struct InfoFile<'a> {\n        #[serde(borrow)]\n        exercises: Vec<ExerciseInfo<'a>>,\n    }\n\n    #[test]\n    fn dirs() {\n        let exercises = toml::de::from_str::<InfoFile>(EMBEDDED_FILES.info_file)\n            .expect(\"Failed to parse `info.toml`\")\n            .exercises;\n\n        assert_eq!(exercises.len(), EMBEDDED_FILES.exercise_files.len());\n\n        for (exercise, exercise_files) in exercises.iter().zip(EMBEDDED_FILES.exercise_files) {\n            assert_eq!(\n                exercise.dir,\n                EMBEDDED_FILES.exercise_dirs[exercise_files.dir_ind].name,\n            );\n        }\n    }\n}\n"
  },
  {
    "path": "src/exercise.rs",
    "content": "use anyhow::Result;\nuse crossterm::{\n    QueueableCommand,\n    style::{Attribute, Color, ResetColor, SetAttribute, SetForegroundColor},\n};\nuse std::io::{self, StdoutLock, Write};\n\nuse crate::{\n    cmd::CmdRunner,\n    term::{self, CountedWrite, file_path, terminal_file_link, write_ansi},\n};\n\n/// The initial capacity of the output buffer.\npub const OUTPUT_CAPACITY: usize = 1 << 14;\n\npub fn solution_link_line(\n    stdout: &mut StdoutLock,\n    solution_path: &str,\n    emit_file_links: bool,\n) -> io::Result<()> {\n    stdout.queue(SetAttribute(Attribute::Bold))?;\n    stdout.write_all(b\"Solution\")?;\n    stdout.queue(ResetColor)?;\n    stdout.write_all(b\" for comparison: \")?;\n    file_path(stdout, Color::Cyan, |writer| {\n        if emit_file_links && let Some(canonical_path) = term::canonicalize(solution_path) {\n            terminal_file_link(writer, solution_path, &canonical_path)\n        } else {\n            writer.stdout().write_all(solution_path.as_bytes())\n        }\n    })?;\n    stdout.write_all(b\"\\n\")\n}\n\n// Run an exercise binary and append its output to the `output` buffer.\n// Compilation must be done before calling this method.\nfn run_bin(\n    bin_name: &str,\n    mut output: Option<&mut Vec<u8>>,\n    cmd_runner: &CmdRunner,\n) -> Result<bool> {\n    if let Some(output) = output.as_deref_mut() {\n        write_ansi(output, SetAttribute(Attribute::Underlined));\n        output.extend_from_slice(b\"Output\");\n        write_ansi(output, ResetColor);\n        output.push(b'\\n');\n    }\n\n    let success = cmd_runner.run_debug_bin(bin_name, output.as_deref_mut())?;\n\n    if let Some(output) = output\n        && !success\n    {\n        // This output is important to show the user that something went wrong.\n        // Otherwise, calling something like `exit(1)` in an exercise without further output\n        // leaves the user confused about why the exercise isn't done yet.\n        write_ansi(output, SetAttribute(Attribute::Bold));\n        write_ansi(output, SetForegroundColor(Color::Red));\n        output.extend_from_slice(b\"The exercise didn't run successfully (nonzero exit code)\");\n        write_ansi(output, ResetColor);\n        output.push(b'\\n');\n    }\n\n    Ok(success)\n}\n\n/// See `info_file::ExerciseInfo`\npub struct Exercise {\n    pub name: &'static str,\n    pub dir: Option<&'static str>,\n    /// Path of the exercise file starting with the `exercises/` directory.\n    pub path: String,\n    pub canonical_path: Option<String>,\n    pub test: bool,\n    pub strict_clippy: bool,\n    pub hint: &'static str,\n    pub done: bool,\n}\n\nimpl Exercise {\n    pub fn terminal_file_link<'a>(\n        &self,\n        writer: &mut impl CountedWrite<'a>,\n        emit_file_links: bool,\n    ) -> io::Result<()> {\n        file_path(writer, Color::Blue, |writer| {\n            if emit_file_links && let Some(canonical_path) = self.canonical_path.as_deref() {\n                terminal_file_link(writer, &self.path, canonical_path)\n            } else {\n                writer.write_str(&self.path)\n            }\n        })\n    }\n}\n\npub trait RunnableExercise {\n    fn name(&self) -> &str;\n    fn dir(&self) -> Option<&str>;\n    fn strict_clippy(&self) -> bool;\n    fn test(&self) -> bool;\n\n    // Compile, check and run the exercise or its solution (depending on `bin_name´).\n    // The output is written to the `output` buffer after clearing it.\n    fn run<const FORCE_STRICT_CLIPPY: bool>(\n        &self,\n        bin_name: &str,\n        mut output: Option<&mut Vec<u8>>,\n        cmd_runner: &CmdRunner,\n    ) -> Result<bool> {\n        if let Some(output) = output.as_deref_mut() {\n            output.clear();\n        }\n\n        let build_success = cmd_runner\n            .cargo(\"build\", bin_name, output.as_deref_mut())\n            .run(\"cargo build …\")?;\n        if !build_success {\n            return Ok(false);\n        }\n\n        // Discard the compiler output because it will be shown again by `cargo test` or Clippy.\n        if let Some(output) = output.as_deref_mut() {\n            output.clear();\n        }\n\n        if self.test() {\n            let output_is_some = output.is_some();\n            let mut test_cmd = cmd_runner.cargo(\"test\", bin_name, output.as_deref_mut());\n            if output_is_some {\n                test_cmd.args([\"--\", \"--color\", \"always\", \"--format\", \"pretty\"]);\n            }\n            let test_success = test_cmd.run(\"cargo test …\")?;\n            if !test_success {\n                run_bin(bin_name, output, cmd_runner)?;\n                return Ok(false);\n            }\n\n            // Discard the compiler output because it will be shown again by Clippy.\n            if let Some(output) = output.as_deref_mut() {\n                output.clear();\n            }\n        }\n\n        let mut clippy_cmd = cmd_runner.cargo(\"clippy\", bin_name, output.as_deref_mut());\n\n        // `--profile test` is required to also check code with `#[cfg(test)]`.\n        if FORCE_STRICT_CLIPPY || self.strict_clippy() {\n            clippy_cmd.args([\"--profile\", \"test\", \"--\", \"-D\", \"warnings\"]);\n        } else {\n            clippy_cmd.args([\"--profile\", \"test\"]);\n        }\n\n        let clippy_success = clippy_cmd.run(\"cargo clippy …\")?;\n        let run_success = run_bin(bin_name, output, cmd_runner)?;\n\n        Ok(clippy_success && run_success)\n    }\n\n    /// Compile, check and run the exercise.\n    /// The output is written to the `output` buffer after clearing it.\n    #[inline]\n    fn run_exercise(&self, output: Option<&mut Vec<u8>>, cmd_runner: &CmdRunner) -> Result<bool> {\n        self.run::<false>(self.name(), output, cmd_runner)\n    }\n\n    /// Compile, check and run the exercise's solution.\n    /// The output is written to the `output` buffer after clearing it.\n    fn run_solution(&self, output: Option<&mut Vec<u8>>, cmd_runner: &CmdRunner) -> Result<bool> {\n        let name = self.name();\n        let mut bin_name = String::with_capacity(name.len() + 4);\n        bin_name.push_str(name);\n        bin_name.push_str(\"_sol\");\n\n        self.run::<true>(&bin_name, output, cmd_runner)\n    }\n\n    fn sol_path(&self) -> String {\n        let name = self.name();\n\n        let mut path = if let Some(dir) = self.dir() {\n            // 14 = 10 + 1 + 3\n            // solutions/ + / + .rs\n            let mut path = String::with_capacity(14 + dir.len() + name.len());\n            path.push_str(\"solutions/\");\n            path.push_str(dir);\n            path.push('/');\n            path\n        } else {\n            // 13 = 10 + 3\n            // solutions/ + .rs\n            let mut path = String::with_capacity(13 + name.len());\n            path.push_str(\"solutions/\");\n            path\n        };\n\n        path.push_str(name);\n        path.push_str(\".rs\");\n\n        path\n    }\n}\n\nimpl RunnableExercise for Exercise {\n    #[inline]\n    fn name(&self) -> &str {\n        self.name\n    }\n\n    #[inline]\n    fn dir(&self) -> Option<&str> {\n        self.dir\n    }\n\n    #[inline]\n    fn strict_clippy(&self) -> bool {\n        self.strict_clippy\n    }\n\n    #[inline]\n    fn test(&self) -> bool {\n        self.test\n    }\n}\n"
  },
  {
    "path": "src/info_file.rs",
    "content": "use anyhow::{Context, Error, Result, bail};\nuse serde::Deserialize;\nuse std::{fs, io::ErrorKind};\n\nuse crate::{embedded::EMBEDDED_FILES, exercise::RunnableExercise};\n\n/// Deserialized from the `info.toml` file.\n#[derive(Deserialize)]\npub struct ExerciseInfo {\n    /// Exercise's unique name.\n    pub name: &'static str,\n    /// Exercise's directory name inside the `exercises/` directory.\n    pub dir: Option<&'static str>,\n    /// Run `cargo test` on the exercise.\n    #[serde(default = \"default_true\")]\n    pub test: bool,\n    /// Deny all Clippy warnings.\n    #[serde(default)]\n    pub strict_clippy: bool,\n    /// The exercise's hint to be shown to the user on request.\n    pub hint: &'static str,\n    /// The exercise is already solved. Ignore it when checking that all exercises are unsolved.\n    #[serde(default)]\n    pub skip_check_unsolved: bool,\n}\n#[inline]\nconst fn default_true() -> bool {\n    true\n}\n\nimpl ExerciseInfo {\n    /// Path to the exercise file starting with the `exercises/` directory.\n    pub fn path(&self) -> String {\n        let mut path = if let Some(dir) = self.dir {\n            // 14 = 10 + 1 + 3\n            // exercises/ + / + .rs\n            let mut path = String::with_capacity(14 + dir.len() + self.name.len());\n            path.push_str(\"exercises/\");\n            path.push_str(dir);\n            path.push('/');\n            path\n        } else {\n            // 13 = 10 + 3\n            // exercises/ + .rs\n            let mut path = String::with_capacity(13 + self.name.len());\n            path.push_str(\"exercises/\");\n            path\n        };\n\n        path.push_str(self.name);\n        path.push_str(\".rs\");\n\n        path\n    }\n}\n\nimpl RunnableExercise for ExerciseInfo {\n    #[inline]\n    fn name(&self) -> &str {\n        self.name\n    }\n\n    #[inline]\n    fn dir(&self) -> Option<&str> {\n        self.dir\n    }\n\n    #[inline]\n    fn strict_clippy(&self) -> bool {\n        self.strict_clippy\n    }\n\n    #[inline]\n    fn test(&self) -> bool {\n        self.test\n    }\n}\n\n/// The deserialized `info.toml` file.\n#[derive(Deserialize)]\npub struct InfoFile {\n    /// For possible breaking changes in the future for community exercises.\n    pub format_version: u8,\n    /// Shown to users when starting with the exercises.\n    pub welcome_message: Option<&'static str>,\n    /// Shown to users after finishing all exercises.\n    pub final_message: Option<&'static str>,\n    /// List of all exercises.\n    pub exercises: Vec<ExerciseInfo>,\n}\n\nimpl InfoFile {\n    /// Official exercises: Parse the embedded `info.toml` file.\n    /// Community exercises: Parse the `info.toml` file in the current directory.\n    pub fn parse() -> Result<Self> {\n        // Read a local `info.toml` if it exists.\n        let slf = match fs::read(\"info.toml\") {\n            Ok(file_content) => {\n                // Remove `\\r` on Windows.\n                // Leaking is fine since the info file is used until the end of the program.\n                let file_content =\n                    String::from_utf8(file_content.into_iter().filter(|c| *c != b'\\r').collect())\n                        .context(\"Failed to parse `info.toml` as UTF8\")?\n                        .leak();\n                toml::de::from_str::<Self>(file_content)\n                    .context(\"Failed to parse the `info.toml` file\")?\n            }\n            Err(e) => {\n                if e.kind() == ErrorKind::NotFound {\n                    return toml::de::from_str(EMBEDDED_FILES.info_file)\n                        .context(\"Failed to parse the embedded `info.toml` file\");\n                }\n\n                return Err(Error::from(e).context(\"Failed to read the `info.toml` file\"));\n            }\n        };\n\n        if slf.exercises.is_empty() {\n            bail!(\"{NO_EXERCISES_ERR}\");\n        }\n\n        Ok(slf)\n    }\n}\n\nconst NO_EXERCISES_ERR: &str = \"There are no exercises yet!\nAdd at least one exercise before testing.\";\n"
  },
  {
    "path": "src/init.rs",
    "content": "use anyhow::{Context, Result, bail};\nuse crossterm::{\n    QueueableCommand,\n    style::{Attribute, Color, ResetColor, SetAttribute, SetForegroundColor},\n};\nuse serde::Deserialize;\nuse std::{\n    env::{current_dir, set_current_dir},\n    fs::{self, create_dir},\n    io::{self, Write},\n    path::Path,\n    process::{Command, Stdio},\n};\n\nuse crate::{\n    cargo_toml::updated_cargo_toml, embedded::EMBEDDED_FILES, exercise::RunnableExercise,\n    info_file::InfoFile, term::press_enter_prompt,\n};\n\n#[derive(Deserialize)]\nstruct CargoLocateProject<'a> {\n    #[serde(borrow)]\n    root: &'a Path,\n}\n\npub fn init() -> Result<()> {\n    let rustlings_dir = Path::new(\"rustlings\");\n    if rustlings_dir.exists() {\n        bail!(RUSTLINGS_DIR_ALREADY_EXISTS_ERR);\n    }\n\n    let locate_project_output = Command::new(\"cargo\")\n        .arg(\"locate-project\")\n        .arg(\"-q\")\n        .arg(\"--workspace\")\n        .stdin(Stdio::null())\n        .stderr(Stdio::null())\n        .output()\n        .context(\n            \"Failed to run the command `cargo locate-project …`\\n\\\n             Did you already install Rust?\\n\\\n             Try running `cargo --version` to diagnose the problem.\",\n        )?;\n\n    if !Command::new(\"cargo\")\n        .arg(\"clippy\")\n        .arg(\"--version\")\n        .stdin(Stdio::null())\n        .stdout(Stdio::null())\n        .stderr(Stdio::null())\n        .status()\n        .context(\"Failed to run the command `cargo clippy --version`\")?\n        .success()\n    {\n        bail!(\n            \"Clippy, the official Rust linter, is missing.\\n\\\n             Please install it first before initializing Rustlings.\"\n        )\n    }\n\n    let mut stdout = io::stdout().lock();\n    let mut init_git = true;\n\n    if locate_project_output.status.success() {\n        if Path::new(\"exercises\").exists() && Path::new(\"solutions\").exists() {\n            bail!(IN_INITIALIZED_DIR_ERR);\n        }\n\n        let workspace_manifest =\n            serde_json::de::from_slice::<CargoLocateProject>(&locate_project_output.stdout)\n                .context(\n                    \"Failed to read the field `root` from the output of `cargo locate-project …`\",\n                )?\n                .root;\n\n        let workspace_manifest_content = fs::read_to_string(workspace_manifest)\n            .with_context(|| format!(\"Failed to read the file {}\", workspace_manifest.display()))?;\n        if !workspace_manifest_content.contains(\"[workspace]\")\n            && !workspace_manifest_content.contains(\"workspace.\")\n        {\n            bail!(\n                \"The current directory is already part of a Cargo project.\\n\\\n                 Please initialize Rustlings in a different directory\"\n            );\n        }\n\n        stdout.write_all(b\"This command will create the directory `rustlings/` as a member of this Cargo workspace.\\n\\\n                           Press ENTER to continue \")?;\n        press_enter_prompt(&mut stdout)?;\n\n        // Make sure \"rustlings\" is added to `workspace.members` by making\n        // Cargo initialize a new project.\n        let status = Command::new(\"cargo\")\n            .arg(\"new\")\n            .arg(\"-q\")\n            .arg(\"--vcs\")\n            .arg(\"none\")\n            .arg(\"rustlings\")\n            .stdin(Stdio::null())\n            .stdout(Stdio::null())\n            .status()?;\n        if !status.success() {\n            bail!(\n                \"Failed to initialize a new Cargo workspace member.\\n\\\n                 Please initialize Rustlings in a different directory\"\n            );\n        }\n\n        stdout.write_all(b\"The directory `rustlings` has been added to `workspace.members` in the `Cargo.toml` file of this Cargo workspace.\\n\")?;\n        fs::remove_dir_all(rustlings_dir)\n            .context(\"Failed to remove the temporary directory `rustlings/`\")?;\n        init_git = false;\n    } else {\n        stdout.write_all(b\"This command will create the directory `rustlings/` which will contain the exercises.\\n\\\n                           Press ENTER to continue \")?;\n        press_enter_prompt(&mut stdout)?;\n    }\n\n    create_dir(rustlings_dir).context(\"Failed to create the `rustlings/` directory\")?;\n    set_current_dir(rustlings_dir)\n        .context(\"Failed to change the current directory to `rustlings/`\")?;\n\n    let info_file = InfoFile::parse()?;\n    EMBEDDED_FILES\n        .init_exercises_dir(&info_file.exercises)\n        .context(\"Failed to initialize the `rustlings/exercises` directory\")?;\n\n    create_dir(\"solutions\").context(\"Failed to create the `solutions/` directory\")?;\n    fs::write(\n        \"solutions/README.md\",\n        include_bytes!(\"../solutions/README.md\"),\n    )\n    .context(\"Failed to create the file rustlings/solutions/README.md\")?;\n    for dir in EMBEDDED_FILES.exercise_dirs {\n        let mut dir_path = String::with_capacity(10 + dir.name.len());\n        dir_path.push_str(\"solutions/\");\n        dir_path.push_str(dir.name);\n        create_dir(&dir_path)\n            .with_context(|| format!(\"Failed to create the directory {dir_path}\"))?;\n    }\n    for exercise_info in &info_file.exercises {\n        let solution_path = exercise_info.sol_path();\n        fs::write(&solution_path, INIT_SOLUTION_FILE)\n            .with_context(|| format!(\"Failed to create the file {solution_path}\"))?;\n    }\n\n    let current_cargo_toml = include_str!(\"../dev-Cargo.toml\");\n    // Skip the first line (comment).\n    let newline_ind = current_cargo_toml\n        .as_bytes()\n        .iter()\n        .position(|c| *c == b'\\n')\n        .context(\"The embedded `Cargo.toml` is empty or contains only one line\")?;\n    let current_cargo_toml = current_cargo_toml\n        .get(newline_ind + 1..)\n        .context(\"The embedded `Cargo.toml` contains only one line\")?;\n    let updated_cargo_toml = updated_cargo_toml(&info_file.exercises, current_cargo_toml, b\"\")\n        .context(\"Failed to generate `Cargo.toml`\")?;\n    fs::write(\"Cargo.toml\", updated_cargo_toml)\n        .context(\"Failed to create the file `rustlings/Cargo.toml`\")?;\n\n    fs::write(\"rust-analyzer.toml\", RUST_ANALYZER_TOML)\n        .context(\"Failed to create the file `rustlings/rust-analyzer.toml`\")?;\n\n    fs::write(\".gitignore\", GITIGNORE)\n        .context(\"Failed to create the file `rustlings/.gitignore`\")?;\n\n    create_dir(\".vscode\").context(\"Failed to create the directory `rustlings/.vscode`\")?;\n    fs::write(\".vscode/extensions.json\", VS_CODE_EXTENSIONS_JSON)\n        .context(\"Failed to create the file `rustlings/.vscode/extensions.json`\")?;\n\n    if init_git && let Ok(dir) = current_dir() {\n        let mut dir = dir.as_path();\n\n        loop {\n            if dir.join(\".git\").exists() || dir.join(\".jj\").exists() {\n                break;\n            }\n\n            if let Some(parent) = dir.parent() {\n                dir = parent;\n            } else {\n                // Ignore any Git error because Git initialization is not required.\n                let _ = Command::new(\"git\")\n                    .arg(\"init\")\n                    .stdin(Stdio::null())\n                    .stdout(Stdio::null())\n                    .stderr(Stdio::null())\n                    .status();\n                break;\n            }\n        }\n    }\n\n    stdout.queue(SetForegroundColor(Color::Green))?;\n    stdout.write_all(\"Initialization done ✓\".as_bytes())?;\n    stdout.queue(ResetColor)?;\n    stdout.write_all(b\"\\n\\n\")?;\n\n    stdout.queue(SetAttribute(Attribute::Bold))?;\n    stdout.write_all(POST_INIT_MSG)?;\n    stdout.queue(ResetColor)?;\n\n    Ok(())\n}\n\nconst INIT_SOLUTION_FILE: &[u8] = b\"fn main() {\n    // DON'T EDIT THIS SOLUTION FILE!\n    // It will be automatically filled after you finish the exercise.\n}\n\";\n\npub const RUST_ANALYZER_TOML: &[u8] = br#\"check.command = \"clippy\"\ncheck.extraArgs = [\"--profile\", \"test\"]\ncargo.targetDir = true\n\"#;\n\nconst GITIGNORE: &[u8] = b\"Cargo.lock\ntarget/\n.vscode/\n\";\n\npub const VS_CODE_EXTENSIONS_JSON: &[u8] = br#\"{\"recommendations\":[\"rust-lang.rust-analyzer\"]}\"#;\n\nconst IN_INITIALIZED_DIR_ERR: &str = \"It looks like Rustlings is already initialized in this directory.\n\nIf you already initialized Rustlings, run the command `rustlings` for instructions on getting started with the exercises.\nOtherwise, please run `rustlings init` again in a different directory.\";\n\nconst RUSTLINGS_DIR_ALREADY_EXISTS_ERR: &str =\n    \"A directory with the name `rustlings` already exists in the current directory.\nYou probably already initialized Rustlings.\nRun `cd rustlings`\nThen run `rustlings` again\";\n\nconst POST_INIT_MSG: &[u8] = b\"Run `cd rustlings` to go into the generated directory.\nThen run `rustlings` to get started.\n\";\n"
  },
  {
    "path": "src/list/scroll_state.rs",
    "content": "pub struct ScrollState {\n    n_rows: usize,\n    max_n_rows_to_display: usize,\n    selected: Option<usize>,\n    offset: usize,\n    scroll_padding: usize,\n    max_scroll_padding: usize,\n}\n\nimpl ScrollState {\n    pub fn new(n_rows: usize, selected: Option<usize>, max_scroll_padding: usize) -> Self {\n        Self {\n            n_rows,\n            max_n_rows_to_display: 0,\n            selected,\n            offset: selected.map_or(0, |selected| selected.saturating_sub(max_scroll_padding)),\n            scroll_padding: 0,\n            max_scroll_padding,\n        }\n    }\n\n    #[inline]\n    pub fn offset(&self) -> usize {\n        self.offset\n    }\n\n    fn update_offset(&mut self) {\n        let Some(selected) = self.selected else {\n            return;\n        };\n\n        let min_offset = (selected + self.scroll_padding)\n            .saturating_sub(self.max_n_rows_to_display.saturating_sub(1));\n        let max_offset = selected.saturating_sub(self.scroll_padding);\n        let global_max_offset = self.n_rows.saturating_sub(self.max_n_rows_to_display);\n\n        self.offset = self\n            .offset\n            .max(min_offset)\n            .min(max_offset)\n            .min(global_max_offset);\n    }\n\n    #[inline]\n    pub fn selected(&self) -> Option<usize> {\n        self.selected\n    }\n\n    pub fn set_selected(&mut self, selected: usize) {\n        self.selected = Some(selected);\n        self.update_offset();\n    }\n\n    pub fn select_next(&mut self) {\n        if let Some(selected) = self.selected {\n            self.set_selected((selected + 1).min(self.n_rows - 1));\n        }\n    }\n\n    pub fn select_previous(&mut self) {\n        if let Some(selected) = self.selected {\n            self.set_selected(selected.saturating_sub(1));\n        }\n    }\n\n    pub fn select_first(&mut self) {\n        if self.n_rows > 0 {\n            self.set_selected(0);\n        }\n    }\n\n    pub fn select_last(&mut self) {\n        if self.n_rows > 0 {\n            self.set_selected(self.n_rows - 1);\n        }\n    }\n\n    pub fn set_n_rows(&mut self, n_rows: usize) {\n        self.n_rows = n_rows;\n\n        if self.n_rows == 0 {\n            self.selected = None;\n            return;\n        }\n\n        self.set_selected(self.selected.map_or(0, |selected| selected.min(n_rows - 1)));\n    }\n\n    #[inline]\n    fn update_scroll_padding(&mut self) {\n        self.scroll_padding = (self.max_n_rows_to_display / 4).min(self.max_scroll_padding);\n    }\n\n    #[inline]\n    pub fn max_n_rows_to_display(&self) -> usize {\n        self.max_n_rows_to_display\n    }\n\n    pub fn set_max_n_rows_to_display(&mut self, max_n_rows_to_display: usize) {\n        self.max_n_rows_to_display = max_n_rows_to_display;\n        self.update_scroll_padding();\n        self.update_offset();\n    }\n}\n"
  },
  {
    "path": "src/list/state.rs",
    "content": "use anyhow::{Context, Result};\nuse crossterm::{\n    QueueableCommand,\n    cursor::{MoveTo, MoveToNextLine},\n    style::{\n        Attribute, Attributes, Color, ResetColor, SetAttribute, SetAttributes, SetForegroundColor,\n    },\n    terminal::{self, BeginSynchronizedUpdate, Clear, ClearType, EndSynchronizedUpdate},\n};\nuse std::{\n    fmt::Write as _,\n    io::{self, StdoutLock, Write},\n};\n\nuse crate::{\n    app_state::AppState,\n    exercise::Exercise,\n    term::{CountedWrite, MaxLenWriter, progress_bar},\n};\n\nuse super::scroll_state::ScrollState;\n\nconst COL_SPACING: usize = 2;\nconst SELECTED_ROW_ATTRIBUTES: Attributes = Attributes::none()\n    .with(Attribute::Reverse)\n    .with(Attribute::Bold);\n\nfn next_ln(stdout: &mut StdoutLock) -> io::Result<()> {\n    stdout\n        .queue(Clear(ClearType::UntilNewLine))?\n        .queue(MoveToNextLine(1))?;\n    Ok(())\n}\n\n#[derive(Copy, Clone, PartialEq, Eq)]\npub enum Filter {\n    Done,\n    Pending,\n    None,\n}\n\npub struct ListState<'a> {\n    /// Footer message to be displayed if not empty.\n    pub message: String,\n    pub search_query: String,\n    app_state: &'a mut AppState,\n    scroll_state: ScrollState,\n    name_col_padding: Vec<u8>,\n    path_col_padding: Vec<u8>,\n    filter: Filter,\n    term_width: u16,\n    term_height: u16,\n    show_footer: bool,\n}\n\nimpl<'a> ListState<'a> {\n    pub fn build(app_state: &'a mut AppState, stdout: &mut StdoutLock) -> Result<Self> {\n        stdout.queue(Clear(ClearType::All))?;\n\n        let name_col_title_len = 4;\n        let path_col_title_len = 4;\n        let (name_col_width, path_col_width) = app_state.exercises().iter().fold(\n            (name_col_title_len, path_col_title_len),\n            |(name_col_width, path_col_width), exercise| {\n                (\n                    name_col_width.max(exercise.name.len()),\n                    path_col_width.max(exercise.path.len()),\n                )\n            },\n        );\n        let name_col_padding = vec![b' '; name_col_width + COL_SPACING];\n        let path_col_padding = vec![b' '; path_col_width];\n\n        let filter = Filter::None;\n        let n_rows_with_filter = app_state.exercises().len();\n        let selected = app_state.current_exercise_ind();\n\n        let (width, height) = terminal::size().context(\"Failed to get the terminal size\")?;\n        let scroll_state = ScrollState::new(n_rows_with_filter, Some(selected), 5);\n\n        let mut slf = Self {\n            message: String::with_capacity(128),\n            search_query: String::new(),\n            app_state,\n            scroll_state,\n            name_col_padding,\n            path_col_padding,\n            filter,\n            // Set by `set_term_size`\n            term_width: 0,\n            term_height: 0,\n            show_footer: true,\n        };\n\n        slf.set_term_size(width, height);\n        slf.draw(stdout)?;\n\n        Ok(slf)\n    }\n\n    pub fn set_term_size(&mut self, width: u16, height: u16) {\n        self.term_width = width;\n        self.term_height = height;\n\n        if height == 0 {\n            return;\n        }\n\n        let header_height = 1;\n        // 1 progress bar, 2 footer message lines.\n        let footer_height = 3;\n        self.show_footer = height > header_height + footer_height;\n\n        self.scroll_state.set_max_n_rows_to_display(\n            height.saturating_sub(header_height + u16::from(self.show_footer) * footer_height)\n                as usize,\n        );\n    }\n\n    fn draw_exercise_name(&self, writer: &mut MaxLenWriter, exercise: &Exercise) -> io::Result<()> {\n        if !self.search_query.is_empty()\n            && let Some((pre_highlight, highlight, post_highlight)) = exercise\n                .name\n                .find(&self.search_query)\n                .and_then(|ind| exercise.name.split_at_checked(ind))\n                .and_then(|(pre_highlight, rest)| {\n                    rest.split_at_checked(self.search_query.len())\n                        .map(|x| (pre_highlight, x.0, x.1))\n                })\n        {\n            writer.write_str(pre_highlight)?;\n            writer.stdout.queue(SetForegroundColor(Color::Magenta))?;\n            writer.write_str(highlight)?;\n            writer.stdout.queue(SetForegroundColor(Color::Reset))?;\n            return writer.write_str(post_highlight);\n        }\n\n        writer.write_str(exercise.name)\n    }\n\n    fn draw_rows(\n        &self,\n        stdout: &mut StdoutLock,\n        filtered_exercises: impl Iterator<Item = (usize, &'a Exercise)>,\n    ) -> io::Result<usize> {\n        let current_exercise_ind = self.app_state.current_exercise_ind();\n        let row_offset = self.scroll_state.offset();\n        let mut n_displayed_rows = 0;\n\n        for (exercise_ind, exercise) in filtered_exercises\n            .skip(row_offset)\n            .take(self.scroll_state.max_n_rows_to_display())\n        {\n            let mut writer = MaxLenWriter::new(stdout, self.term_width as usize);\n\n            if self.scroll_state.selected() == Some(row_offset + n_displayed_rows) {\n                // The crab emoji has the width of two ascii chars.\n                writer.add_to_len(2);\n                writer.stdout.write_all(\"🦀\".as_bytes())?;\n                writer\n                    .stdout\n                    .queue(SetAttributes(SELECTED_ROW_ATTRIBUTES))?;\n            } else {\n                writer.write_ascii(b\"  \")?;\n            }\n\n            if exercise_ind == current_exercise_ind {\n                writer.stdout.queue(SetForegroundColor(Color::Red))?;\n                writer.write_ascii(b\">>>>>>>  \")?;\n            } else {\n                writer.write_ascii(b\"         \")?;\n            }\n\n            if exercise.done {\n                writer.stdout.queue(SetForegroundColor(Color::Green))?;\n                writer.write_ascii(b\"DONE   \")?;\n            } else {\n                writer.stdout.queue(SetForegroundColor(Color::Yellow))?;\n                writer.write_ascii(b\"PENDING\")?;\n            }\n            writer.stdout.queue(SetForegroundColor(Color::Reset))?;\n            writer.write_ascii(b\"  \")?;\n\n            self.draw_exercise_name(&mut writer, exercise)?;\n\n            writer.write_ascii(&self.name_col_padding[exercise.name.len()..])?;\n\n            exercise.terminal_file_link(&mut writer, self.app_state.emit_file_links())?;\n\n            writer.write_ascii(&self.path_col_padding[exercise.path.len()..])?;\n\n            next_ln(stdout)?;\n            stdout.queue(ResetColor)?;\n            n_displayed_rows += 1;\n        }\n\n        Ok(n_displayed_rows)\n    }\n\n    pub fn draw(&mut self, stdout: &mut StdoutLock) -> io::Result<()> {\n        if self.term_height == 0 {\n            return Ok(());\n        }\n\n        stdout.queue(BeginSynchronizedUpdate)?.queue(MoveTo(0, 0))?;\n\n        // Header\n        let mut writer = MaxLenWriter::new(stdout, self.term_width as usize);\n        writer.write_ascii(b\"  Current  State    Name\")?;\n        writer.write_ascii(&self.name_col_padding[4..])?;\n        writer.write_ascii(b\"Path\")?;\n        next_ln(stdout)?;\n\n        // Rows\n        let iter = self.app_state.exercises().iter().enumerate();\n        let n_displayed_rows = match self.filter {\n            Filter::Done => self.draw_rows(stdout, iter.filter(|(_, exercise)| exercise.done))?,\n            Filter::Pending => {\n                self.draw_rows(stdout, iter.filter(|(_, exercise)| !exercise.done))?\n            }\n            Filter::None => self.draw_rows(stdout, iter)?,\n        };\n\n        for _ in 0..self.scroll_state.max_n_rows_to_display() - n_displayed_rows {\n            next_ln(stdout)?;\n        }\n\n        if self.show_footer {\n            progress_bar(\n                &mut MaxLenWriter::new(stdout, self.term_width as usize),\n                self.app_state.n_done(),\n                self.app_state.exercises().len() as u32,\n                self.term_width,\n            )?;\n            next_ln(stdout)?;\n\n            let mut writer = MaxLenWriter::new(stdout, self.term_width as usize);\n            if self.message.is_empty() {\n                // Help footer message\n                if self.scroll_state.selected().is_some() {\n                    writer.write_str(\"↓/j ↑/k home/g end/G | <c>ontinue at | <r>eset exercise\")?;\n                    next_ln(stdout)?;\n                    writer = MaxLenWriter::new(stdout, self.term_width as usize);\n\n                    writer.write_ascii(b\"<s>earch | filter \")?;\n                } else {\n                    // Nothing selected (and nothing shown), so only display filter and quit.\n                    writer.write_ascii(b\"filter \")?;\n                }\n\n                match self.filter {\n                    Filter::Done => {\n                        writer\n                            .stdout\n                            .queue(SetForegroundColor(Color::Magenta))?\n                            .queue(SetAttribute(Attribute::Underlined))?;\n                        writer.write_ascii(b\"<d>one\")?;\n                        writer.stdout.queue(ResetColor)?;\n                        writer.write_ascii(b\"/<p>ending\")?;\n                    }\n                    Filter::Pending => {\n                        writer.write_ascii(b\"<d>one/\")?;\n                        writer\n                            .stdout\n                            .queue(SetForegroundColor(Color::Magenta))?\n                            .queue(SetAttribute(Attribute::Underlined))?;\n                        writer.write_ascii(b\"<p>ending\")?;\n                        writer.stdout.queue(ResetColor)?;\n                    }\n                    Filter::None => writer.write_ascii(b\"<d>one/<p>ending\")?,\n                }\n\n                writer.write_ascii(b\" | <q>uit list\")?;\n            } else {\n                writer.stdout.queue(SetForegroundColor(Color::Magenta))?;\n                writer.write_str(&self.message)?;\n                stdout.queue(ResetColor)?;\n                next_ln(stdout)?;\n            }\n\n            next_ln(stdout)?;\n        }\n\n        stdout.queue(EndSynchronizedUpdate)?.flush()\n    }\n\n    fn update_rows(&mut self) {\n        let n_rows = match self.filter {\n            Filter::Done => self\n                .app_state\n                .exercises()\n                .iter()\n                .filter(|exercise| exercise.done)\n                .count(),\n            Filter::Pending => self\n                .app_state\n                .exercises()\n                .iter()\n                .filter(|exercise| !exercise.done)\n                .count(),\n            Filter::None => self.app_state.exercises().len(),\n        };\n\n        self.scroll_state.set_n_rows(n_rows);\n    }\n\n    #[inline]\n    pub fn filter(&self) -> Filter {\n        self.filter\n    }\n\n    pub fn set_filter(&mut self, filter: Filter) {\n        self.filter = filter;\n        self.update_rows();\n    }\n\n    #[inline]\n    pub fn select_next(&mut self) {\n        self.scroll_state.select_next();\n    }\n\n    #[inline]\n    pub fn select_previous(&mut self) {\n        self.scroll_state.select_previous();\n    }\n\n    #[inline]\n    pub fn select_first(&mut self) {\n        self.scroll_state.select_first();\n    }\n\n    #[inline]\n    pub fn select_last(&mut self) {\n        self.scroll_state.select_last();\n    }\n\n    fn selected_to_exercise_ind(&self, selected: usize) -> Result<usize> {\n        match self.filter {\n            Filter::Done => self\n                .app_state\n                .exercises()\n                .iter()\n                .enumerate()\n                .filter(|(_, exercise)| exercise.done)\n                .nth(selected)\n                .context(\"Invalid selection index\")\n                .map(|(ind, _)| ind),\n            Filter::Pending => self\n                .app_state\n                .exercises()\n                .iter()\n                .enumerate()\n                .filter(|(_, exercise)| !exercise.done)\n                .nth(selected)\n                .context(\"Invalid selection index\")\n                .map(|(ind, _)| ind),\n            Filter::None => Ok(selected),\n        }\n    }\n\n    pub fn reset_selected(&mut self) -> Result<()> {\n        let Some(selected) = self.scroll_state.selected() else {\n            self.message.push_str(\"Nothing selected to reset!\");\n            return Ok(());\n        };\n\n        let exercise_ind = self.selected_to_exercise_ind(selected)?;\n        let exercise_name = self.app_state.reset_exercise_by_ind(exercise_ind)?;\n        write!(\n            self.message,\n            \"The exercise `{exercise_name}` has been reset\",\n        )?;\n        self.update_rows();\n\n        Ok(())\n    }\n\n    pub fn apply_search_query(&mut self) {\n        self.message.push_str(\"search:\");\n        self.message.push_str(&self.search_query);\n        self.message.push('|');\n\n        if self.search_query.is_empty() {\n            return;\n        }\n\n        let is_search_result = |exercise: &Exercise| exercise.name.contains(&self.search_query);\n        let mut iter = self.app_state.exercises().iter();\n        let ind = match self.filter {\n            Filter::None => iter.position(is_search_result),\n            Filter::Done => iter\n                .filter(|exercise| exercise.done)\n                .position(is_search_result),\n            Filter::Pending => iter\n                .filter(|exercise| !exercise.done)\n                .position(is_search_result),\n        };\n\n        match ind {\n            Some(exercise_ind) => self.scroll_state.set_selected(exercise_ind),\n            None => self.message.push_str(\" (not found)\"),\n        }\n    }\n\n    // Return `true` if there was something to select.\n    pub fn selected_to_current_exercise(&mut self) -> Result<bool> {\n        let Some(selected) = self.scroll_state.selected() else {\n            self.message.push_str(\"Nothing selected to continue at!\");\n            return Ok(false);\n        };\n\n        let exercise_ind = self.selected_to_exercise_ind(selected)?;\n        self.app_state.set_current_exercise_ind(exercise_ind)?;\n\n        Ok(true)\n    }\n}\n"
  },
  {
    "path": "src/list.rs",
    "content": "use anyhow::{Context, Result};\nuse crossterm::{\n    QueueableCommand, cursor,\n    event::{\n        self, DisableMouseCapture, EnableMouseCapture, Event, KeyCode, KeyEventKind, MouseEventKind,\n    },\n    terminal::{\n        DisableLineWrap, EnableLineWrap, EnterAlternateScreen, LeaveAlternateScreen,\n        disable_raw_mode, enable_raw_mode,\n    },\n};\nuse std::io::{self, StdoutLock, Write};\n\nuse crate::app_state::AppState;\n\nuse self::state::{Filter, ListState};\n\nmod scroll_state;\nmod state;\n\nfn handle_list(app_state: &mut AppState, stdout: &mut StdoutLock) -> Result<()> {\n    let mut list_state = ListState::build(app_state, stdout)?;\n    let mut is_searching = false;\n\n    loop {\n        match event::read().context(\"Failed to read terminal event\")? {\n            Event::Key(key) => {\n                match key.kind {\n                    KeyEventKind::Release => continue,\n                    KeyEventKind::Press | KeyEventKind::Repeat => (),\n                }\n\n                list_state.message.clear();\n\n                if is_searching {\n                    match key.code {\n                        KeyCode::Esc | KeyCode::Enter => {\n                            is_searching = false;\n                            list_state.search_query.clear();\n                        }\n                        KeyCode::Char(c) => {\n                            list_state.search_query.push(c);\n                            list_state.apply_search_query();\n                        }\n                        KeyCode::Backspace => {\n                            list_state.search_query.pop();\n                            list_state.apply_search_query();\n                        }\n                        _ => continue,\n                    }\n\n                    list_state.draw(stdout)?;\n                    continue;\n                }\n\n                match key.code {\n                    KeyCode::Char('q') => return Ok(()),\n                    KeyCode::Down | KeyCode::Char('j') => list_state.select_next(),\n                    KeyCode::Up | KeyCode::Char('k') => list_state.select_previous(),\n                    KeyCode::Home | KeyCode::Char('g') => list_state.select_first(),\n                    KeyCode::End | KeyCode::Char('G') => list_state.select_last(),\n                    KeyCode::Char('d') => {\n                        if list_state.filter() == Filter::Done {\n                            list_state.set_filter(Filter::None);\n                            list_state.message.push_str(\"Disabled filter DONE\");\n                        } else {\n                            list_state.set_filter(Filter::Done);\n                            list_state.message.push_str(\n                                \"Enabled filter DONE │ Press d again to disable the filter\",\n                            );\n                        }\n                    }\n                    KeyCode::Char('p') => {\n                        if list_state.filter() == Filter::Pending {\n                            list_state.set_filter(Filter::None);\n                            list_state.message.push_str(\"Disabled filter PENDING\");\n                        } else {\n                            list_state.set_filter(Filter::Pending);\n                            list_state.message.push_str(\n                                \"Enabled filter PENDING │ Press p again to disable the filter\",\n                            );\n                        }\n                    }\n                    KeyCode::Char('r') => list_state.reset_selected()?,\n                    KeyCode::Char('c') => {\n                        if list_state.selected_to_current_exercise()? {\n                            return Ok(());\n                        }\n                    }\n                    KeyCode::Char('s' | '/') => {\n                        is_searching = true;\n                        list_state.apply_search_query();\n                    }\n                    // Redraw to remove the message.\n                    KeyCode::Esc => (),\n                    _ => continue,\n                }\n            }\n            Event::Mouse(event) => match event.kind {\n                MouseEventKind::ScrollDown => list_state.select_next(),\n                MouseEventKind::ScrollUp => list_state.select_previous(),\n                _ => continue,\n            },\n            Event::Resize(width, height) => list_state.set_term_size(width, height),\n            // Ignore\n            Event::FocusGained | Event::FocusLost => continue,\n        }\n\n        list_state.draw(stdout)?;\n    }\n}\n\npub fn list(app_state: &mut AppState) -> Result<()> {\n    let mut stdout = io::stdout().lock();\n    stdout\n        .queue(EnterAlternateScreen)?\n        .queue(cursor::Hide)?\n        .queue(DisableLineWrap)?\n        .queue(EnableMouseCapture)?;\n    enable_raw_mode()?;\n\n    let res = handle_list(app_state, &mut stdout);\n\n    // Restore the terminal even if we got an error.\n    stdout\n        .queue(LeaveAlternateScreen)?\n        .queue(cursor::Show)?\n        .queue(EnableLineWrap)?\n        .queue(DisableMouseCapture)?\n        .flush()?;\n    disable_raw_mode()?;\n\n    res\n}\n"
  },
  {
    "path": "src/main.rs",
    "content": "use anyhow::{Context, Result, bail};\nuse app_state::StateFileStatus;\nuse clap::{Parser, Subcommand};\nuse std::{\n    io::{self, IsTerminal, Write},\n    path::Path,\n    process::ExitCode,\n};\nuse term::{clear_terminal, press_enter_prompt};\n\nuse self::{app_state::AppState, dev::DevCommands, info_file::InfoFile};\n\nmod app_state;\nmod cargo_toml;\nmod cmd;\nmod dev;\nmod embedded;\nmod exercise;\nmod info_file;\nmod init;\nmod list;\nmod run;\nmod term;\nmod watch;\n\nconst CURRENT_FORMAT_VERSION: u8 = 1;\n\n/// Rustlings is a collection of small exercises to get you used to writing and reading Rust code\n#[derive(Parser)]\n#[command(version)]\nstruct Args {\n    #[command(subcommand)]\n    command: Option<Subcommands>,\n    /// Manually run the current exercise using `r` in the watch mode.\n    /// Only use this if Rustlings fails to detect exercise file changes.\n    #[arg(long)]\n    manual_run: bool,\n}\n\n#[derive(Subcommand)]\nenum Subcommands {\n    /// Initialize the official Rustlings exercises\n    Init,\n    /// Run a single exercise. Runs the next pending exercise if the exercise name is not specified\n    Run {\n        /// The name of the exercise\n        name: Option<String>,\n    },\n    /// Check all the exercises, marking them as done or pending accordingly.\n    CheckAll,\n    /// Reset a single exercise\n    Reset {\n        /// The name of the exercise\n        name: String,\n    },\n    /// Show a hint. Shows the hint of the next pending exercise if the exercise name is not specified\n    Hint {\n        /// The name of the exercise\n        name: Option<String>,\n    },\n    /// Commands for developing (community) Rustlings exercises\n    #[command(subcommand)]\n    Dev(DevCommands),\n}\n\nfn main() -> Result<ExitCode> {\n    let args = Args::parse();\n\n    if cfg!(not(debug_assertions)) && Path::new(\"dev/rustlings-repo.txt\").exists() {\n        bail!(\"{OLD_METHOD_ERR}\");\n    }\n\n    'priority_cmd: {\n        match args.command {\n            Some(Subcommands::Init) => init::init().context(\"Initialization failed\")?,\n            Some(Subcommands::Dev(dev_command)) => dev_command.run()?,\n            _ => break 'priority_cmd,\n        }\n\n        return Ok(ExitCode::SUCCESS);\n    }\n\n    if !Path::new(\"exercises\").is_dir() {\n        println!(\"{PRE_INIT_MSG}\");\n        return Ok(ExitCode::FAILURE);\n    }\n\n    let info_file = InfoFile::parse()?;\n\n    if info_file.format_version > CURRENT_FORMAT_VERSION {\n        bail!(FORMAT_VERSION_HIGHER_ERR);\n    }\n\n    let (mut app_state, state_file_status) = AppState::new(\n        info_file.exercises,\n        info_file.final_message.unwrap_or_default(),\n    )?;\n\n    // Show the welcome message if the state file doesn't exist yet.\n    if let Some(welcome_message) = info_file.welcome_message {\n        match state_file_status {\n            StateFileStatus::NotRead => {\n                let mut stdout = io::stdout().lock();\n                clear_terminal(&mut stdout)?;\n\n                let welcome_message = welcome_message.trim_ascii();\n                write!(\n                    stdout,\n                    \"{welcome_message}\\n\\n\\\n                     Press ENTER to continue \"\n                )?;\n                press_enter_prompt(&mut stdout)?;\n                clear_terminal(&mut stdout)?;\n                // Flush to be able to show errors occurring before printing a newline to stdout.\n                stdout.flush()?;\n            }\n            StateFileStatus::Read => (),\n        }\n    }\n\n    match args.command {\n        None => {\n            if !io::stdout().is_terminal() {\n                bail!(\"Unsupported or missing terminal/TTY\");\n            }\n\n            let notify_exercise_names = if args.manual_run {\n                None\n            } else {\n                // For the notify event handler thread.\n                // Leaking is fine since the slice is used until the end of the program.\n                Some(\n                    &*app_state\n                        .exercises()\n                        .iter()\n                        .map(|exercise| exercise.name.as_bytes())\n                        .collect::<Vec<_>>()\n                        .leak(),\n                )\n            };\n\n            watch::watch(&mut app_state, notify_exercise_names)?;\n        }\n        Some(Subcommands::Run { name }) => {\n            if let Some(name) = name {\n                app_state.set_current_exercise_by_name(&name)?;\n            }\n            return run::run(&mut app_state);\n        }\n        Some(Subcommands::CheckAll) => {\n            let mut stdout = io::stdout().lock();\n            if let Some(first_pending_exercise_ind) = app_state.check_all_exercises(&mut stdout)? {\n                if app_state.current_exercise().done {\n                    app_state.set_current_exercise_ind(first_pending_exercise_ind)?;\n                }\n\n                stdout.write_all(b\"\\n\\n\")?;\n                let pending = app_state.n_pending();\n                if pending == 1 {\n                    stdout.write_all(b\"One exercise pending: \")?;\n                } else {\n                    write!(\n                        stdout,\n                        \"{pending}/{} exercises pending. The first: \",\n                        app_state.exercises().len(),\n                    )?;\n                }\n                app_state\n                    .current_exercise()\n                    .terminal_file_link(&mut stdout, app_state.emit_file_links())?;\n                stdout.write_all(b\"\\n\")?;\n\n                return Ok(ExitCode::FAILURE);\n            }\n\n            app_state.render_final_message(&mut stdout)?;\n        }\n        Some(Subcommands::Reset { name }) => {\n            app_state.set_current_exercise_by_name(&name)?;\n            let exercise_path = app_state.reset_current_exercise()?;\n            println!(\"The exercise {exercise_path} has been reset\");\n        }\n        Some(Subcommands::Hint { name }) => {\n            if let Some(name) = name {\n                app_state.set_current_exercise_by_name(&name)?;\n            }\n            println!(\"{}\", app_state.current_exercise().hint);\n        }\n        // Handled in an earlier match.\n        Some(Subcommands::Init | Subcommands::Dev(_)) => (),\n    }\n\n    Ok(ExitCode::SUCCESS)\n}\n\nconst OLD_METHOD_ERR: &str =\n    \"You are trying to run Rustlings using the old method before version 6.\nThe new method doesn't include cloning the Rustlings' repository.\nPlease follow the instructions in `README.md`:\nhttps://github.com/rust-lang/rustlings#getting-started\";\n\nconst FORMAT_VERSION_HIGHER_ERR: &str =\n    \"The format version specified in the `info.toml` file is higher than the last one supported.\nIt is possible that you have an outdated version of Rustlings.\nTry to install the latest Rustlings version first.\";\n\nconst PRE_INIT_MSG: &str = r\"\n       Welcome to...\n                 _   _ _\n  _ __ _   _ ___| |_| (_)_ __   __ _ ___\n | '__| | | / __| __| | | '_ \\ / _` / __|\n | |  | |_| \\__ \\ |_| | | | | | (_| \\__ \\\n |_|   \\__,_|___/\\__|_|_|_| |_|\\__, |___/\n                               |___/\n\nThe `exercises/` directory couldn't be found in the current directory.\nIf you are just starting with Rustlings, run the command `rustlings init` to initialize it.\";\n"
  },
  {
    "path": "src/run.rs",
    "content": "use anyhow::Result;\nuse crossterm::{\n    QueueableCommand,\n    style::{Color, ResetColor, SetForegroundColor},\n};\nuse std::{\n    io::{self, Write},\n    process::ExitCode,\n};\n\nuse crate::{\n    app_state::{AppState, ExercisesProgress},\n    exercise::{OUTPUT_CAPACITY, RunnableExercise, solution_link_line},\n};\n\npub fn run(app_state: &mut AppState) -> Result<ExitCode> {\n    let exercise = app_state.current_exercise();\n    let mut output = Vec::with_capacity(OUTPUT_CAPACITY);\n    let success = exercise.run_exercise(Some(&mut output), app_state.cmd_runner())?;\n\n    let mut stdout = io::stdout().lock();\n    stdout.write_all(&output)?;\n\n    if !success {\n        app_state.set_pending(app_state.current_exercise_ind())?;\n\n        stdout.write_all(b\"Ran \")?;\n        app_state\n            .current_exercise()\n            .terminal_file_link(&mut stdout, app_state.emit_file_links())?;\n        stdout.write_all(b\" with errors\\n\")?;\n\n        return Ok(ExitCode::FAILURE);\n    }\n\n    stdout.queue(SetForegroundColor(Color::Green))?;\n    stdout.write_all(\"✓ Successfully ran \".as_bytes())?;\n    stdout.write_all(exercise.path.as_bytes())?;\n    stdout.queue(ResetColor)?;\n    stdout.write_all(b\"\\n\")?;\n\n    if let Some(solution_path) = app_state.current_solution_path()? {\n        stdout.write_all(b\"\\n\")?;\n        solution_link_line(&mut stdout, &solution_path, app_state.emit_file_links())?;\n        stdout.write_all(b\"\\n\")?;\n    }\n\n    match app_state.done_current_exercise::<false>(&mut stdout)? {\n        ExercisesProgress::NewPending | ExercisesProgress::CurrentPending => {\n            stdout.write_all(b\"Next exercise: \")?;\n            app_state\n                .current_exercise()\n                .terminal_file_link(&mut stdout, app_state.emit_file_links())?;\n            stdout.write_all(b\"\\n\")?;\n        }\n        ExercisesProgress::AllDone => (),\n    }\n\n    Ok(ExitCode::SUCCESS)\n}\n"
  },
  {
    "path": "src/term.rs",
    "content": "use crossterm::{\n    Command, QueueableCommand,\n    cursor::MoveTo,\n    style::{Attribute, Color, ResetColor, SetAttribute, SetForegroundColor},\n    terminal::{Clear, ClearType},\n};\nuse std::{\n    fmt, fs,\n    io::{self, BufRead, StdoutLock, Write},\n};\n\nuse crate::app_state::CheckProgress;\n\npub struct MaxLenWriter<'a, 'lock> {\n    pub stdout: &'a mut StdoutLock<'lock>,\n    len: usize,\n    max_len: usize,\n}\n\nimpl<'a, 'lock> MaxLenWriter<'a, 'lock> {\n    #[inline]\n    pub fn new(stdout: &'a mut StdoutLock<'lock>, max_len: usize) -> Self {\n        Self {\n            stdout,\n            len: 0,\n            max_len,\n        }\n    }\n\n    // Additional is for emojis that take more space.\n    #[inline]\n    pub fn add_to_len(&mut self, additional: usize) {\n        self.len += additional;\n    }\n}\n\npub trait CountedWrite<'lock> {\n    fn write_ascii(&mut self, ascii: &[u8]) -> io::Result<()>;\n    fn write_str(&mut self, unicode: &str) -> io::Result<()>;\n    fn stdout(&mut self) -> &mut StdoutLock<'lock>;\n}\n\nimpl<'lock> CountedWrite<'lock> for MaxLenWriter<'_, 'lock> {\n    fn write_ascii(&mut self, ascii: &[u8]) -> io::Result<()> {\n        let n = ascii.len().min(self.max_len.saturating_sub(self.len));\n        if n > 0 {\n            self.stdout.write_all(&ascii[..n])?;\n            self.len += n;\n        }\n        Ok(())\n    }\n\n    fn write_str(&mut self, unicode: &str) -> io::Result<()> {\n        if let Some((ind, c)) = unicode\n            .char_indices()\n            .take(self.max_len.saturating_sub(self.len))\n            .last()\n        {\n            self.stdout\n                .write_all(&unicode.as_bytes()[..ind + c.len_utf8()])?;\n            self.len += ind + 1;\n        }\n\n        Ok(())\n    }\n\n    #[inline]\n    fn stdout(&mut self) -> &mut StdoutLock<'lock> {\n        self.stdout\n    }\n}\n\nimpl<'a> CountedWrite<'a> for StdoutLock<'a> {\n    #[inline]\n    fn write_ascii(&mut self, ascii: &[u8]) -> io::Result<()> {\n        self.write_all(ascii)\n    }\n\n    #[inline]\n    fn write_str(&mut self, unicode: &str) -> io::Result<()> {\n        self.write_all(unicode.as_bytes())\n    }\n\n    #[inline]\n    fn stdout(&mut self) -> &mut StdoutLock<'a> {\n        self\n    }\n}\n\npub struct CheckProgressVisualizer<'a, 'lock> {\n    stdout: &'a mut StdoutLock<'lock>,\n    n_cols: usize,\n}\n\nimpl<'a, 'lock> CheckProgressVisualizer<'a, 'lock> {\n    const CHECKING_COLOR: Color = Color::Blue;\n    const DONE_COLOR: Color = Color::Green;\n    const PENDING_COLOR: Color = Color::Red;\n\n    pub fn build(stdout: &'a mut StdoutLock<'lock>, term_width: u16) -> io::Result<Self> {\n        clear_terminal(stdout)?;\n        stdout.write_all(\"Checking all exercises…\\n\".as_bytes())?;\n\n        // Legend\n        stdout.write_all(b\"Color of exercise number: \")?;\n        stdout.queue(SetForegroundColor(Self::CHECKING_COLOR))?;\n        stdout.write_all(b\"Checking\")?;\n        stdout.queue(ResetColor)?;\n        stdout.write_all(b\" - \")?;\n        stdout.queue(SetForegroundColor(Self::DONE_COLOR))?;\n        stdout.write_all(b\"Done\")?;\n        stdout.queue(ResetColor)?;\n        stdout.write_all(b\" - \")?;\n        stdout.queue(SetForegroundColor(Self::PENDING_COLOR))?;\n        stdout.write_all(b\"Pending\")?;\n        stdout.queue(ResetColor)?;\n        stdout.write_all(b\"\\n\")?;\n\n        // Exercise numbers with up to 3 digits.\n        // +1 because the last column doesn't end with a whitespace.\n        let n_cols = usize::from(term_width + 1) / 4;\n\n        Ok(Self { stdout, n_cols })\n    }\n\n    pub fn update(&mut self, progresses: &[CheckProgress]) -> io::Result<()> {\n        self.stdout.queue(MoveTo(0, 2))?;\n\n        let mut exercise_num = 1;\n        for exercise_progress in progresses {\n            match exercise_progress {\n                CheckProgress::None => (),\n                CheckProgress::Checking => {\n                    self.stdout\n                        .queue(SetForegroundColor(Self::CHECKING_COLOR))?;\n                }\n                CheckProgress::Done => {\n                    self.stdout.queue(SetForegroundColor(Self::DONE_COLOR))?;\n                }\n                CheckProgress::Pending => {\n                    self.stdout.queue(SetForegroundColor(Self::PENDING_COLOR))?;\n                }\n            }\n\n            write!(self.stdout, \"{exercise_num:<3}\")?;\n            self.stdout.queue(ResetColor)?;\n\n            if exercise_num != progresses.len() {\n                if exercise_num % self.n_cols == 0 {\n                    self.stdout.write_all(b\"\\n\")?;\n                } else {\n                    self.stdout.write_all(b\" \")?;\n                }\n\n                exercise_num += 1;\n            }\n        }\n\n        self.stdout.flush()\n    }\n}\n\npub struct ProgressCounter<'a, 'lock> {\n    stdout: &'a mut StdoutLock<'lock>,\n    total: usize,\n    counter: usize,\n}\n\nimpl<'a, 'lock> ProgressCounter<'a, 'lock> {\n    pub fn new(stdout: &'a mut StdoutLock<'lock>, total: usize) -> io::Result<Self> {\n        write!(stdout, \"Progress: 0/{total}\")?;\n        stdout.flush()?;\n\n        Ok(Self {\n            stdout,\n            total,\n            counter: 0,\n        })\n    }\n\n    pub fn increment(&mut self) -> io::Result<()> {\n        self.counter += 1;\n        write!(self.stdout, \"\\rProgress: {}/{}\", self.counter, self.total)?;\n        self.stdout.flush()\n    }\n}\n\nimpl Drop for ProgressCounter<'_, '_> {\n    fn drop(&mut self) {\n        let _ = self.stdout.write_all(b\"\\n\\n\");\n    }\n}\n\npub fn progress_bar<'a>(\n    writer: &mut impl CountedWrite<'a>,\n    progress: u32,\n    total: u32,\n    term_width: u16,\n) -> io::Result<()> {\n    const PREFIX: &[u8] = b\"Progress: [\";\n    const PREFIX_WIDTH: u16 = PREFIX.len() as u16;\n    const POSTFIX_WIDTH: u16 = \"] xxx/xxx\".len() as u16;\n    const WRAPPER_WIDTH: u16 = PREFIX_WIDTH + POSTFIX_WIDTH;\n    const MIN_LINE_WIDTH: u16 = WRAPPER_WIDTH + 4;\n\n    debug_assert!(total <= 999);\n    debug_assert!(progress <= total);\n\n    if term_width < MIN_LINE_WIDTH {\n        writer.write_ascii(b\"Progress: \")?;\n        // Integers are in ASCII.\n        return writer.write_ascii(format!(\"{progress}/{total}\").as_bytes());\n    }\n\n    let stdout = writer.stdout();\n    stdout.write_all(PREFIX)?;\n\n    // Use u32 to prevent the intermediate multiplication from overflowing\n    let width = u32::from(term_width - WRAPPER_WIDTH);\n    let filled = (width * progress) / total;\n\n    stdout.queue(SetForegroundColor(Color::Green))?;\n    for _ in 0..filled {\n        stdout.write_all(b\"#\")?;\n    }\n\n    if filled < width {\n        stdout.write_all(b\">\")?;\n\n        let width_minus_filled = width - filled;\n        if width_minus_filled > 1 {\n            stdout.queue(SetForegroundColor(Color::Red))?;\n            for _ in 1..width_minus_filled {\n                stdout.write_all(b\"-\")?;\n            }\n        }\n    }\n\n    stdout.queue(SetForegroundColor(Color::Reset))?;\n\n    write!(stdout, \"] {progress:>3}/{total}\")\n}\n\npub fn clear_terminal(stdout: &mut StdoutLock) -> io::Result<()> {\n    stdout\n        .queue(MoveTo(0, 0))?\n        .queue(Clear(ClearType::All))?\n        .queue(Clear(ClearType::Purge))\n        .map(|_| ())\n}\n\npub fn press_enter_prompt(stdout: &mut StdoutLock) -> io::Result<()> {\n    stdout.flush()?;\n    io::stdin().lock().read_until(b'\\n', &mut Vec::new())?;\n    stdout.write_all(b\"\\n\")\n}\n\n/// Canonicalize, convert to string and remove verbatim part on Windows.\npub fn canonicalize(path: &str) -> Option<String> {\n    fs::canonicalize(path)\n        .ok()?\n        .into_os_string()\n        .into_string()\n        .ok()\n        .map(|mut path| {\n            // Windows itself can't handle its verbatim paths.\n            if cfg!(windows) && path.as_bytes().starts_with(br\"\\\\?\\\") {\n                path.drain(..4);\n            }\n\n            path\n        })\n}\n\npub fn file_path<'a, W: CountedWrite<'a>>(\n    writer: &mut W,\n    color: Color,\n    f: impl FnOnce(&mut W) -> io::Result<()>,\n) -> io::Result<()> {\n    writer\n        .stdout()\n        .queue(SetForegroundColor(color))?\n        .queue(SetAttribute(Attribute::Underlined))?;\n\n    f(writer)?;\n\n    writer\n        .stdout()\n        .queue(SetForegroundColor(Color::Reset))?\n        .queue(SetAttribute(Attribute::NoUnderline))?;\n\n    Ok(())\n}\n\npub fn terminal_file_link<'a>(\n    writer: &mut impl CountedWrite<'a>,\n    path: &str,\n    canonical_path: &str,\n) -> io::Result<()> {\n    writer.stdout().write_all(b\"\\x1b]8;;file://\")?;\n    writer.stdout().write_all(canonical_path.as_bytes())?;\n    writer.stdout().write_all(b\"\\x1b\\\\\")?;\n    // Only this part is visible.\n    writer.write_str(path)?;\n    writer.stdout().write_all(b\"\\x1b]8;;\\x1b\\\\\")\n}\n\npub fn write_ansi(output: &mut Vec<u8>, command: impl Command) {\n    struct FmtWriter<'a>(&'a mut Vec<u8>);\n\n    impl fmt::Write for FmtWriter<'_> {\n        fn write_str(&mut self, s: &str) -> fmt::Result {\n            self.0.extend_from_slice(s.as_bytes());\n            Ok(())\n        }\n    }\n\n    let _ = command.write_ansi(&mut FmtWriter(output));\n}\n"
  },
  {
    "path": "src/watch/notify_event.rs",
    "content": "use anyhow::{Context, Result};\nuse notify::{\n    Event, EventKind,\n    event::{AccessKind, AccessMode, MetadataKind, ModifyKind, RenameMode},\n};\nuse std::{\n    sync::{\n        atomic::Ordering::Relaxed,\n        mpsc::{RecvTimeoutError, Sender, SyncSender, sync_channel},\n    },\n    thread,\n    time::Duration,\n};\n\nuse super::{EXERCISE_RUNNING, WatchEvent};\n\nconst DEBOUNCE_DURATION: Duration = Duration::from_millis(200);\n\npub struct NotifyEventHandler {\n    error_sender: Sender<WatchEvent>,\n    // Sends the index of the updated exercise.\n    update_sender: SyncSender<usize>,\n    // Used to report which exercise was modified.\n    exercise_names: &'static [&'static [u8]],\n}\n\nimpl NotifyEventHandler {\n    pub fn build(\n        watch_event_sender: Sender<WatchEvent>,\n        exercise_names: &'static [&'static [u8]],\n    ) -> Result<Self> {\n        let (update_sender, update_receiver) = sync_channel(0);\n        let error_sender = watch_event_sender.clone();\n\n        // Debouncer\n        thread::Builder::new()\n            .spawn(move || {\n                let mut exercise_updated = vec![false; exercise_names.len()];\n\n                loop {\n                    match update_receiver.recv_timeout(DEBOUNCE_DURATION) {\n                        Ok(exercise_ind) => exercise_updated[exercise_ind] = true,\n                        Err(RecvTimeoutError::Timeout) => {\n                            for (exercise_ind, updated) in exercise_updated.iter_mut().enumerate() {\n                                if *updated {\n                                    if watch_event_sender\n                                        .send(WatchEvent::FileChange { exercise_ind })\n                                        .is_err()\n                                    {\n                                        break;\n                                    }\n\n                                    *updated = false;\n                                }\n                            }\n                        }\n                        Err(RecvTimeoutError::Disconnected) => break,\n                    }\n                }\n            })\n            .context(\"Failed to spawn a thread to debounce file changes\")?;\n\n        Ok(Self {\n            error_sender,\n            update_sender,\n            exercise_names,\n        })\n    }\n}\n\nimpl notify::EventHandler for NotifyEventHandler {\n    fn handle_event(&mut self, input_event: notify::Result<Event>) {\n        if EXERCISE_RUNNING.load(Relaxed) {\n            return;\n        }\n\n        let input_event = match input_event {\n            Ok(v) => v,\n            Err(e) => {\n                // An error occurs when the receiver is dropped.\n                // After dropping the receiver, the watcher guard should also be dropped.\n                let _ = self.error_sender.send(WatchEvent::NotifyErr(e));\n                return;\n            }\n        };\n\n        match input_event.kind {\n            EventKind::Any => (),\n            EventKind::Modify(modify_kind) => match modify_kind {\n                ModifyKind::Any | ModifyKind::Data(_) => (),\n                ModifyKind::Name(rename_mode) => match rename_mode {\n                    RenameMode::Any | RenameMode::To => (),\n                    RenameMode::From | RenameMode::Both | RenameMode::Other => return,\n                },\n                ModifyKind::Metadata(metadata_kind) => match metadata_kind {\n                    MetadataKind::Any | MetadataKind::WriteTime => (),\n                    MetadataKind::AccessTime\n                    | MetadataKind::Permissions\n                    | MetadataKind::Ownership\n                    | MetadataKind::Extended\n                    | MetadataKind::Other => return,\n                },\n                ModifyKind::Other => return,\n            },\n            EventKind::Access(access_kind) => match access_kind {\n                AccessKind::Any => (),\n                AccessKind::Close(access_mode) => match access_mode {\n                    AccessMode::Any | AccessMode::Write => (),\n                    AccessMode::Execute | AccessMode::Read | AccessMode::Other => return,\n                },\n                AccessKind::Read | AccessKind::Open(_) | AccessKind::Other => return,\n            },\n            EventKind::Create(_) | EventKind::Remove(_) | EventKind::Other => return,\n        }\n\n        let _ = input_event\n            .paths\n            .into_iter()\n            .filter_map(|path| {\n                let file_name = path.file_name()?.to_str()?.as_bytes();\n\n                let [file_name_without_ext @ .., b'.', b'r', b's'] = file_name else {\n                    return None;\n                };\n\n                self.exercise_names\n                    .iter()\n                    .position(|exercise_name| *exercise_name == file_name_without_ext)\n            })\n            .try_for_each(|exercise_ind| self.update_sender.send(exercise_ind));\n    }\n}\n"
  },
  {
    "path": "src/watch/state.rs",
    "content": "use anyhow::{Context, Result};\nuse crossterm::{\n    QueueableCommand,\n    style::{\n        Attribute, Attributes, Color, ResetColor, SetAttribute, SetAttributes, SetForegroundColor,\n    },\n    terminal,\n};\nuse std::{\n    io::{self, Read, StdoutLock, Write},\n    sync::mpsc::{Sender, SyncSender, sync_channel},\n    thread,\n};\n\nuse crate::{\n    app_state::{AppState, ExercisesProgress},\n    clear_terminal,\n    exercise::{OUTPUT_CAPACITY, RunnableExercise, solution_link_line},\n    term::progress_bar,\n};\n\nuse super::{InputPauseGuard, WatchEvent, terminal_event::terminal_event_handler};\n\nconst HEADING_ATTRIBUTES: Attributes = Attributes::none()\n    .with(Attribute::Bold)\n    .with(Attribute::Underlined);\n\n#[derive(PartialEq, Eq)]\nenum DoneStatus {\n    DoneWithSolution(String),\n    DoneWithoutSolution,\n    Pending,\n}\n\npub struct WatchState<'a> {\n    app_state: &'a mut AppState,\n    output: Vec<u8>,\n    show_hint: bool,\n    done_status: DoneStatus,\n    manual_run: bool,\n    term_width: u16,\n    terminal_event_unpause_sender: SyncSender<()>,\n}\n\nimpl<'a> WatchState<'a> {\n    pub fn build(\n        app_state: &'a mut AppState,\n        watch_event_sender: Sender<WatchEvent>,\n        manual_run: bool,\n    ) -> Result<Self> {\n        let term_width = terminal::size()\n            .context(\"Failed to get the terminal size\")?\n            .0;\n\n        let (terminal_event_unpause_sender, terminal_event_unpause_receiver) = sync_channel(0);\n\n        thread::Builder::new()\n            .spawn(move || {\n                terminal_event_handler(\n                    watch_event_sender,\n                    terminal_event_unpause_receiver,\n                    manual_run,\n                );\n            })\n            .context(\"Failed to spawn a thread to handle terminal events\")?;\n\n        Ok(Self {\n            app_state,\n            output: Vec::with_capacity(OUTPUT_CAPACITY),\n            show_hint: false,\n            done_status: DoneStatus::Pending,\n            manual_run,\n            term_width,\n            terminal_event_unpause_sender,\n        })\n    }\n\n    pub fn run_current_exercise(&mut self, stdout: &mut StdoutLock) -> Result<()> {\n        // Ignore any input until running the exercise is done.\n        let _input_pause_guard = InputPauseGuard::scoped_pause();\n\n        self.show_hint = false;\n\n        writeln!(\n            stdout,\n            \"\\nChecking the exercise `{}`. Please wait…\",\n            self.app_state.current_exercise().name,\n        )?;\n\n        let success = self\n            .app_state\n            .current_exercise()\n            .run_exercise(Some(&mut self.output), self.app_state.cmd_runner())?;\n        self.output.push(b'\\n');\n        if success {\n            self.done_status =\n                if let Some(solution_path) = self.app_state.current_solution_path()? {\n                    DoneStatus::DoneWithSolution(solution_path)\n                } else {\n                    DoneStatus::DoneWithoutSolution\n                };\n        } else {\n            self.app_state\n                .set_pending(self.app_state.current_exercise_ind())?;\n\n            self.done_status = DoneStatus::Pending;\n        }\n\n        self.render(stdout)?;\n        Ok(())\n    }\n\n    pub fn reset_exercise(&mut self, stdout: &mut StdoutLock) -> Result<()> {\n        clear_terminal(stdout)?;\n\n        stdout.write_all(b\"Resetting will undo all your changes to the file \")?;\n        stdout.write_all(self.app_state.current_exercise().path.as_bytes())?;\n        stdout.write_all(b\"\\nReset (y/n)? \")?;\n        stdout.flush()?;\n\n        {\n            let mut stdin = io::stdin().lock();\n            let mut answer = [0];\n            loop {\n                stdin\n                    .read_exact(&mut answer)\n                    .context(\"Failed to read the user's input\")?;\n\n                match answer[0] {\n                    b'y' | b'Y' => {\n                        self.app_state.reset_current_exercise()?;\n\n                        // The file watcher reruns the exercise otherwise.\n                        if self.manual_run {\n                            self.run_current_exercise(stdout)?;\n                        }\n                    }\n                    b'n' | b'N' => self.render(stdout)?,\n                    _ => continue,\n                }\n\n                break;\n            }\n        }\n\n        self.terminal_event_unpause_sender.send(())?;\n\n        Ok(())\n    }\n\n    pub fn handle_file_change(\n        &mut self,\n        exercise_ind: usize,\n        stdout: &mut StdoutLock,\n    ) -> Result<()> {\n        if self.app_state.current_exercise_ind() != exercise_ind {\n            return Ok(());\n        }\n\n        self.run_current_exercise(stdout)\n    }\n\n    /// Move on to the next exercise if the current one is done.\n    pub fn next_exercise(&mut self, stdout: &mut StdoutLock) -> Result<ExercisesProgress> {\n        match self.done_status {\n            DoneStatus::DoneWithSolution(_) | DoneStatus::DoneWithoutSolution => (),\n            DoneStatus::Pending => return Ok(ExercisesProgress::CurrentPending),\n        }\n\n        self.app_state.done_current_exercise::<true>(stdout)\n    }\n\n    fn show_prompt(&self, stdout: &mut StdoutLock) -> io::Result<()> {\n        if self.done_status != DoneStatus::Pending {\n            stdout.queue(SetAttribute(Attribute::Bold))?;\n            stdout.write_all(b\"n\")?;\n            stdout.queue(ResetColor)?;\n            stdout.write_all(b\":\")?;\n            stdout.queue(SetAttribute(Attribute::Underlined))?;\n            stdout.write_all(b\"next\")?;\n            stdout.queue(ResetColor)?;\n            stdout.write_all(b\" / \")?;\n        }\n\n        let mut show_key = |key, postfix| {\n            stdout.queue(SetAttribute(Attribute::Bold))?;\n            stdout.write_all(&[key])?;\n            stdout.queue(ResetColor)?;\n            stdout.write_all(postfix)\n        };\n\n        if self.manual_run {\n            show_key(b'r', b\":run / \")?;\n        }\n\n        if !self.show_hint {\n            show_key(b'h', b\":hint / \")?;\n        }\n\n        show_key(b'l', b\":list / \")?;\n        show_key(b'c', b\":check all / \")?;\n        show_key(b'x', b\":reset / \")?;\n        show_key(b'q', b\":quit ? \")?;\n\n        stdout.flush()\n    }\n\n    pub fn render(&self, stdout: &mut StdoutLock) -> io::Result<()> {\n        // Prevent having the first line shifted if clearing wasn't successful.\n        stdout.write_all(b\"\\n\")?;\n        clear_terminal(stdout)?;\n\n        stdout.write_all(&self.output)?;\n\n        if self.show_hint {\n            stdout\n                .queue(SetAttributes(HEADING_ATTRIBUTES))?\n                .queue(SetForegroundColor(Color::Cyan))?;\n            stdout.write_all(b\"Hint\")?;\n            stdout.queue(ResetColor)?;\n            stdout.write_all(b\"\\n\")?;\n\n            stdout.write_all(self.app_state.current_exercise().hint.as_bytes())?;\n            stdout.write_all(b\"\\n\\n\")?;\n        }\n\n        if self.done_status != DoneStatus::Pending {\n            stdout\n                .queue(SetAttribute(Attribute::Bold))?\n                .queue(SetForegroundColor(Color::Green))?;\n            stdout.write_all(\"Exercise done ✓\".as_bytes())?;\n            stdout.queue(ResetColor)?;\n            stdout.write_all(b\"\\n\")?;\n\n            if let DoneStatus::DoneWithSolution(solution_path) = &self.done_status {\n                solution_link_line(stdout, solution_path, self.app_state.emit_file_links())?;\n            }\n\n            stdout.write_all(\n                \"When done experimenting, enter `n` to move on to the next exercise 🦀\\n\\n\"\n                    .as_bytes(),\n            )?;\n        }\n\n        progress_bar(\n            stdout,\n            self.app_state.n_done(),\n            self.app_state.exercises().len() as u32,\n            self.term_width,\n        )?;\n\n        stdout.write_all(b\"\\nCurrent exercise: \")?;\n        self.app_state\n            .current_exercise()\n            .terminal_file_link(stdout, self.app_state.emit_file_links())?;\n        stdout.write_all(b\"\\n\\n\")?;\n\n        self.show_prompt(stdout)?;\n\n        Ok(())\n    }\n\n    pub fn show_hint(&mut self, stdout: &mut StdoutLock) -> io::Result<()> {\n        if !self.show_hint {\n            self.show_hint = true;\n            self.render(stdout)?;\n        }\n\n        Ok(())\n    }\n\n    pub fn check_all_exercises(&mut self, stdout: &mut StdoutLock) -> Result<ExercisesProgress> {\n        // Ignore any input until checking all exercises is done.\n        let _input_pause_guard = InputPauseGuard::scoped_pause();\n\n        if let Some(first_pending_exercise_ind) = self.app_state.check_all_exercises(stdout)? {\n            // Only change exercise if the current one is done.\n            if self.app_state.current_exercise().done {\n                self.app_state\n                    .set_current_exercise_ind(first_pending_exercise_ind)?;\n                Ok(ExercisesProgress::NewPending)\n            } else {\n                Ok(ExercisesProgress::CurrentPending)\n            }\n        } else {\n            self.app_state.render_final_message(stdout)?;\n            Ok(ExercisesProgress::AllDone)\n        }\n    }\n\n    pub fn update_term_width(&mut self, width: u16, stdout: &mut StdoutLock) -> io::Result<()> {\n        if self.term_width != width {\n            self.term_width = width;\n            self.render(stdout)?;\n        }\n\n        Ok(())\n    }\n}\n"
  },
  {
    "path": "src/watch/terminal_event.rs",
    "content": "use crossterm::event::{self, Event, KeyCode, KeyEventKind};\nuse std::sync::{\n    atomic::Ordering::Relaxed,\n    mpsc::{Receiver, Sender},\n};\n\nuse super::{EXERCISE_RUNNING, WatchEvent};\n\npub enum InputEvent {\n    Next,\n    Run,\n    Hint,\n    List,\n    CheckAll,\n    Reset,\n    Quit,\n}\n\npub fn terminal_event_handler(\n    sender: Sender<WatchEvent>,\n    unpause_receiver: Receiver<()>,\n    manual_run: bool,\n) {\n    let last_watch_event = loop {\n        match event::read() {\n            Ok(Event::Key(key)) => {\n                match key.kind {\n                    KeyEventKind::Release | KeyEventKind::Repeat => continue,\n                    KeyEventKind::Press => (),\n                }\n\n                if EXERCISE_RUNNING.load(Relaxed) {\n                    continue;\n                }\n\n                let input_event = match key.code {\n                    KeyCode::Char('n') => InputEvent::Next,\n                    KeyCode::Char('r') if manual_run => InputEvent::Run,\n                    KeyCode::Char('h') => InputEvent::Hint,\n                    KeyCode::Char('l') => break WatchEvent::Input(InputEvent::List),\n                    KeyCode::Char('c') => InputEvent::CheckAll,\n                    KeyCode::Char('x') => {\n                        if sender.send(WatchEvent::Input(InputEvent::Reset)).is_err() {\n                            return;\n                        }\n\n                        // Pause input until quitting the confirmation prompt.\n                        if unpause_receiver.recv().is_err() {\n                            return;\n                        }\n\n                        continue;\n                    }\n                    KeyCode::Char('q') => break WatchEvent::Input(InputEvent::Quit),\n                    _ => continue,\n                };\n\n                if sender.send(WatchEvent::Input(input_event)).is_err() {\n                    return;\n                }\n            }\n            Ok(Event::Resize(width, _)) => {\n                if sender.send(WatchEvent::TerminalResize { width }).is_err() {\n                    return;\n                }\n            }\n            Ok(Event::FocusGained | Event::FocusLost | Event::Mouse(_)) => (),\n            Err(e) => break WatchEvent::TerminalEventErr(e),\n        }\n    };\n\n    let _ = sender.send(last_watch_event);\n}\n"
  },
  {
    "path": "src/watch.rs",
    "content": "use anyhow::{Error, Result};\nuse notify::{Config, RecommendedWatcher, RecursiveMode, Watcher};\nuse std::{\n    io::{self, Write},\n    path::Path,\n    sync::{\n        atomic::{AtomicBool, Ordering::Relaxed},\n        mpsc::channel,\n    },\n    time::Duration,\n};\n\nuse crate::{\n    app_state::{AppState, ExercisesProgress},\n    list,\n};\n\nuse self::{notify_event::NotifyEventHandler, state::WatchState, terminal_event::InputEvent};\n\nmod notify_event;\nmod state;\nmod terminal_event;\n\nstatic EXERCISE_RUNNING: AtomicBool = AtomicBool::new(false);\n\n// Private unit type to force using the constructor function.\n#[must_use = \"When the guard is dropped, the input is unpaused\"]\npub struct InputPauseGuard(());\n\nimpl InputPauseGuard {\n    #[inline]\n    pub fn scoped_pause() -> Self {\n        EXERCISE_RUNNING.store(true, Relaxed);\n        Self(())\n    }\n}\n\nimpl Drop for InputPauseGuard {\n    #[inline]\n    fn drop(&mut self) {\n        EXERCISE_RUNNING.store(false, Relaxed);\n    }\n}\n\nenum WatchEvent {\n    Input(InputEvent),\n    FileChange { exercise_ind: usize },\n    TerminalResize { width: u16 },\n    NotifyErr(notify::Error),\n    TerminalEventErr(io::Error),\n}\n\n/// Returned by the watch mode to indicate what to do afterwards.\n#[must_use]\nenum WatchExit {\n    /// Exit the program.\n    Shutdown,\n    /// Enter the list mode and restart the watch mode afterwards.\n    List,\n}\n\nfn run_watch(\n    app_state: &mut AppState,\n    notify_exercise_names: Option<&'static [&'static [u8]]>,\n) -> Result<WatchExit> {\n    let (watch_event_sender, watch_event_receiver) = channel();\n\n    let mut manual_run = false;\n    // Prevent dropping the guard until the end of the function.\n    // Otherwise, the file watcher exits.\n    let _watcher_guard = if let Some(exercise_names) = notify_exercise_names {\n        let notify_event_handler =\n            NotifyEventHandler::build(watch_event_sender.clone(), exercise_names)?;\n\n        let mut watcher = RecommendedWatcher::new(\n            notify_event_handler,\n            Config::default()\n                .with_follow_symlinks(false)\n                .with_poll_interval(Duration::from_secs(1)),\n        )\n        .inspect_err(|_| eprintln!(\"{NOTIFY_ERR}\"))?;\n\n        watcher\n            .watch(Path::new(\"exercises\"), RecursiveMode::Recursive)\n            .inspect_err(|_| eprintln!(\"{NOTIFY_ERR}\"))?;\n\n        Some(watcher)\n    } else {\n        manual_run = true;\n        None\n    };\n\n    let mut watch_state = WatchState::build(app_state, watch_event_sender, manual_run)?;\n    let mut stdout = io::stdout().lock();\n\n    watch_state.run_current_exercise(&mut stdout)?;\n\n    while let Ok(event) = watch_event_receiver.recv() {\n        match event {\n            WatchEvent::Input(InputEvent::Next) => match watch_state.next_exercise(&mut stdout)? {\n                ExercisesProgress::AllDone => break,\n                ExercisesProgress::NewPending => watch_state.run_current_exercise(&mut stdout)?,\n                ExercisesProgress::CurrentPending => (),\n            },\n            WatchEvent::Input(InputEvent::Run) => watch_state.run_current_exercise(&mut stdout)?,\n            WatchEvent::Input(InputEvent::Hint) => watch_state.show_hint(&mut stdout)?,\n            WatchEvent::Input(InputEvent::List) => return Ok(WatchExit::List),\n            WatchEvent::Input(InputEvent::CheckAll) => match watch_state\n                .check_all_exercises(&mut stdout)?\n            {\n                ExercisesProgress::AllDone => break,\n                ExercisesProgress::NewPending => watch_state.run_current_exercise(&mut stdout)?,\n                ExercisesProgress::CurrentPending => watch_state.render(&mut stdout)?,\n            },\n            WatchEvent::Input(InputEvent::Reset) => watch_state.reset_exercise(&mut stdout)?,\n            WatchEvent::Input(InputEvent::Quit) => {\n                stdout.write_all(QUIT_MSG)?;\n                break;\n            }\n            WatchEvent::FileChange { exercise_ind } => {\n                watch_state.handle_file_change(exercise_ind, &mut stdout)?;\n            }\n            WatchEvent::TerminalResize { width } => {\n                watch_state.update_term_width(width, &mut stdout)?;\n            }\n            WatchEvent::NotifyErr(e) => return Err(Error::from(e).context(NOTIFY_ERR)),\n            WatchEvent::TerminalEventErr(e) => {\n                return Err(Error::from(e).context(\"Terminal event listener failed\"));\n            }\n        }\n    }\n\n    Ok(WatchExit::Shutdown)\n}\n\nfn watch_list_loop(\n    app_state: &mut AppState,\n    notify_exercise_names: Option<&'static [&'static [u8]]>,\n) -> Result<()> {\n    loop {\n        match run_watch(app_state, notify_exercise_names)? {\n            WatchExit::Shutdown => break Ok(()),\n            // It is much easier to exit the watch mode, launch the list mode and then restart\n            // the watch mode instead of trying to pause the watch threads and correct the\n            // watch state.\n            WatchExit::List => list::list(app_state)?,\n        }\n    }\n}\n\n/// `notify_exercise_names` as None activates the manual run mode.\npub fn watch(\n    app_state: &mut AppState,\n    notify_exercise_names: Option<&'static [&'static [u8]]>,\n) -> Result<()> {\n    #[cfg(not(windows))]\n    {\n        let stdin_fd = rustix::stdio::stdin();\n        let mut termios = rustix::termios::tcgetattr(stdin_fd)?;\n        let original_local_modes = termios.local_modes;\n        // Disable stdin line buffering and hide input.\n        termios.local_modes -=\n            rustix::termios::LocalModes::ICANON | rustix::termios::LocalModes::ECHO;\n        rustix::termios::tcsetattr(stdin_fd, rustix::termios::OptionalActions::Now, &termios)?;\n\n        let res = watch_list_loop(app_state, notify_exercise_names);\n\n        termios.local_modes = original_local_modes;\n        rustix::termios::tcsetattr(stdin_fd, rustix::termios::OptionalActions::Now, &termios)?;\n\n        res\n    }\n\n    #[cfg(windows)]\n    watch_list_loop(app_state, notify_exercise_names)\n}\n\nconst QUIT_MSG: &[u8] = b\"\n\nWe hope you're enjoying learning Rust!\nIf you want to continue working on the exercises at a later point, you can simply run `rustlings` again in this directory.\n\";\n\nconst NOTIFY_ERR: &str = \"\nThe automatic detection of exercise file changes failed :(\nPlease try running `rustlings` again.\n\nIf you keep getting this error, run `rustlings --manual-run` to deactivate the file watcher.\nYou need to manually trigger running the current exercise using `r` then.\n\";\n"
  },
  {
    "path": "tests/integration_tests.rs",
    "content": "use std::{\n    process::{Command, Stdio},\n    str::from_utf8,\n};\n\nenum Output<'a> {\n    FullStdout(&'a [u8]),\n    PartialStdout(&'a str),\n    PartialStderr(&'a str),\n}\n\nuse Output::*;\n\n#[derive(Default)]\nstruct Cmd<'a> {\n    current_dir: Option<&'a str>,\n    args: &'a [&'a str],\n    output: Option<Output<'a>>,\n}\n\nimpl<'a> Cmd<'a> {\n    #[inline]\n    fn current_dir(&mut self, current_dir: &'a str) -> &mut Self {\n        self.current_dir = Some(current_dir);\n        self\n    }\n\n    #[inline]\n    fn args(&mut self, args: &'a [&'a str]) -> &mut Self {\n        self.args = args;\n        self\n    }\n\n    #[inline]\n    fn output(&mut self, output: Output<'a>) -> &mut Self {\n        self.output = Some(output);\n        self\n    }\n\n    #[track_caller]\n    fn assert(&self, success: bool) {\n        let mut cmd = Command::new(env!(\"CARGO_BIN_EXE_rustlings\"));\n\n        if let Some(current_dir) = self.current_dir {\n            cmd.current_dir(current_dir);\n        }\n\n        cmd.args(self.args).stdin(Stdio::null());\n\n        let output = cmd.output().unwrap();\n        match self.output {\n            None => (),\n            Some(FullStdout(stdout)) => {\n                assert_eq!(output.stdout, stdout);\n            }\n            Some(PartialStdout(stdout)) => {\n                assert!(from_utf8(&output.stdout).unwrap().contains(stdout));\n            }\n            Some(PartialStderr(stderr)) => {\n                assert!(from_utf8(&output.stderr).unwrap().contains(stderr));\n            }\n        };\n\n        if output.status.success() != success {\n            panic!(\n                \"{cmd:?}\\n\\nstdout:\\n{}\\n\\nstderr:\\n{}\",\n                from_utf8(&output.stdout).unwrap(),\n                from_utf8(&output.stderr).unwrap(),\n            );\n        }\n    }\n\n    #[inline]\n    #[track_caller]\n    fn success(&self) {\n        self.assert(true);\n    }\n\n    #[inline]\n    #[track_caller]\n    fn fail(&self) {\n        self.assert(false);\n    }\n}\n\n#[test]\nfn run_compilation_success() {\n    Cmd::default()\n        .current_dir(\"tests/test_exercises\")\n        .args(&[\"run\", \"compilation_success\"])\n        .success();\n}\n\n#[test]\nfn run_compilation_failure() {\n    Cmd::default()\n        .current_dir(\"tests/test_exercises\")\n        .args(&[\"run\", \"compilation_failure\"])\n        .fail();\n}\n\n#[test]\nfn run_test_success() {\n    Cmd::default()\n        .current_dir(\"tests/test_exercises\")\n        .args(&[\"run\", \"test_success\"])\n        .output(PartialStdout(\"\\nOutput from `main` function\\n\"))\n        .success();\n}\n\n#[test]\nfn run_test_failure() {\n    Cmd::default()\n        .current_dir(\"tests/test_exercises\")\n        .args(&[\"run\", \"test_failure\"])\n        .fail();\n}\n\n#[test]\nfn run_exercise_not_in_info() {\n    Cmd::default()\n        .current_dir(\"tests/test_exercises\")\n        .args(&[\"run\", \"not_in_info\"])\n        .fail();\n}\n\n#[test]\nfn reset_without_exercise_name() {\n    Cmd::default().args(&[\"reset\"]).fail();\n}\n\n#[test]\nfn hint() {\n    Cmd::default()\n        .current_dir(\"tests/test_exercises\")\n        .args(&[\"hint\", \"test_failure\"])\n        .output(FullStdout(b\"The answer to everything: 42\\n\"))\n        .success();\n}\n\n#[test]\nfn init() {\n    let test_dir = tempfile::TempDir::new().unwrap();\n    let test_dir = test_dir.path().to_str().unwrap();\n\n    Cmd::default().current_dir(test_dir).fail();\n\n    Cmd::default()\n        .current_dir(test_dir)\n        .args(&[\"init\"])\n        .success();\n\n    // Running `init` after a successful initialization.\n    Cmd::default()\n        .current_dir(test_dir)\n        .args(&[\"init\"])\n        .output(PartialStderr(\"`cd rustlings`\"))\n        .fail();\n\n    let initialized_dir = format!(\"{test_dir}/rustlings\");\n\n    // Running `init` in the initialized directory.\n    Cmd::default()\n        .current_dir(&initialized_dir)\n        .args(&[\"init\"])\n        .output(PartialStderr(\"already initialized\"))\n        .fail();\n}\n"
  },
  {
    "path": "tests/test_exercises/dev/Cargo.toml",
    "content": "bin = [\n  { name = \"compilation_success\", path = \"../exercises/compilation_success.rs\" },\n  { name = \"compilation_failure\", path = \"../exercises/compilation_failure.rs\" },\n  { name = \"test_success\", path = \"../exercises/test_success.rs\" },\n  { name = \"test_failure\", path = \"../exercises/test_failure.rs\" },\n]\n\n[package]\nname = \"test_exercises\"\nedition = \"2024\"\npublish = false\n"
  },
  {
    "path": "tests/test_exercises/exercises/compilation_failure.rs",
    "content": "fn main() {\n    let\n}"
  },
  {
    "path": "tests/test_exercises/exercises/compilation_success.rs",
    "content": "fn main() {}\n"
  },
  {
    "path": "tests/test_exercises/exercises/not_in_info.rs",
    "content": "fn main() {}\n"
  },
  {
    "path": "tests/test_exercises/exercises/test_failure.rs",
    "content": "fn main() {}\n\n#[cfg(test)]\nmod tests {\n    #[test]\n    fn fails() {\n        assert!(false);\n    }\n}\n"
  },
  {
    "path": "tests/test_exercises/exercises/test_success.rs",
    "content": "fn main() {\n    println!(\"Output from `main` function\");\n}\n\n#[cfg(test)]\nmod tests {\n    #[test]\n    fn passes() {}\n}\n"
  },
  {
    "path": "tests/test_exercises/info.toml",
    "content": "format_version = 1\n\n[[exercises]]\nname = \"compilation_success\"\ntest = false\nhint = \"\"\n\n[[exercises]]\nname = \"compilation_failure\"\ntest = false\nhint = \"\"\n\n[[exercises]]\nname = \"test_success\"\nhint = \"\"\n\n[[exercises]]\nname = \"test_failure\"\nhint = \"The answer to everything: 42\"\n"
  },
  {
    "path": "website/.gitignore",
    "content": "/node_modules/\n/package-lock.json\n\n/public/\n\n/static/main.css\n/static/processed_images/\n"
  },
  {
    "path": "website/config.toml",
    "content": "base_url = \"https://rustlings.rust-lang.org\"\ntitle = \"Rustlings\"\ndescription = \"Small exercises to get you used to reading and writing Rust code!\"\n\ncompile_sass = false\nbuild_search_index = false\n\n[markdown]\ninsert_anchor_links = \"heading\"\n\n[markdown.highlighting]\ntheme = \"dracula\"\n\n[extra]\nlogo_path = \"images/happy_ferris.svg\"\n\n[[extra.menu_items]]\nname = \"Rustlings\"\nurl = \"@/_index.md\"\n[[extra.menu_items]]\nname = \"Setup\"\nurl = \"@/setup/index.md\"\n[[extra.menu_items]]\nname = \"Usage\"\nurl = \"@/usage/index.md\"\n[[extra.menu_items]]\nname = \"Community Exercises\"\nurl = \"@/community-exercises/index.md\"\n[[extra.menu_items]]\nname = \"Q&A\"\nurl = \"https://github.com/rust-lang/rustlings/discussions/categories/q-a?discussions_q=\"\n\n[[extra.footer_items]]\nname = \"Repository\"\nurl = \"https://github.com/rust-lang/rustlings\"\n[[extra.footer_items]]\nname = \"Changelog\"\nurl = \"https://github.com/rust-lang/rustlings/blob/main/CHANGELOG.md\"\n[[extra.footer_items]]\nname = \"MIT License\"\nurl = \"https://github.com/rust-lang/rustlings/blob/main/LICENSE\"\n"
  },
  {
    "path": "website/content/_index.md",
    "content": "+++\n+++\n\nSmall exercises to get you used to reading and writing [Rust](https://www.rust-lang.org) code - _Recommended in parallel to reading [the official Rust book](https://doc.rust-lang.org/book) 📚️_\n\n<script src=\"https://asciinema.org/a/719805.js\" id=\"asciicast-719805\" async=\"true\"></script>\n\n## Quick start\n\n```bash\n# Installation\ncargo install rustlings\n# Initialization\nrustlings init\n# Moving into new directory\ncd rustlings\n# Starting Rustlings\nrustlings\n```\n\nVisit the [**setup**](@/setup/index.md) page for more details 🧰\n"
  },
  {
    "path": "website/content/community-exercises/index.md",
    "content": "+++\ntitle = \"Community Exercises\"\n+++\n\n## List of Community Exercises\n\n- 🇯🇵 [Japanese Rustlings](https://github.com/sotanengel/rustlings-jp)：A Japanese translation of the Rustlings exercises.\n- 🇨🇳 [Simplified Chinese Rustlings](https://github.com/SandmeyerX/rustlings-zh-cn): A simplified Chinese translation of the Rustlings exercises.\n\n> You can use the same `rustlings` program that you installed with `cargo install rustlings` to run community exercises.\n\n## Creating Community Exercises\n\nRustling's support for community exercises allows you to create your own exercises to focus on some specific topic.\nYou could also offer a translation of the original Rustlings exercises as community exercises.\n\n### Getting Started\n\nTo create community exercises, install Rustlings and run `rustlings dev new PROJECT_NAME`.\nThis command will, similar to `cargo new PROJECT_NAME`, create the template directory `PROJECT_NAME` with all what you need to get started.\n\n_Read the comments_ in the generated `info.toml` file to understand its format.\nIt allows you to set a custom welcome and final message and specify the metadata of every exercise.\n\n### Creating an Exercise\n\nHere is an example of the metadata of one exercise:\n\n```toml\n[[exercises]]\nname = \"intro1\"\nhint = \"\"\"\nTo finish this exercise, you need to …\nThese links might help you …\"\"\"\n```\n\nAfter entering this in `info.toml`, create the file `intro1.rs` in the `exercises/` directory.\nThe exercise needs to contain a `main` function, but it can be empty.\nAdding tests is recommended.\nLook at the official Rustlings exercises for inspiration.\n\nYou can optionally add a solution file `intro1.rs` to the `solutions/` directory.\n\nNow, run `rustlings dev check`.\nIt will tell you about any issues with your exercises.\nFor example, it will tell you to run `rustlings dev update` to update the `Cargo.toml` file to include the new exercise `intro1`.\n\n`rustlings dev check` will also run your solutions (if you have any) to make sure that they run successfully.\n\nThat's it!\nYou finished your first exercise 🎉\n\n### Cargo.toml\n\nExcept of the `bin` list, you can modify the `Cargo.toml` file as you want.\n\n> The `bin` list is automatically updated by running `rustlings dev update`\n\n- You can add dependencies in the `[dependencies]` table.\n- You might want to [configure some lints](https://doc.rust-lang.org/cargo/reference/manifest.html#the-lints-section) for all exercises. You can do so in the `[lints.rust]` and `[lints.clippy]` tables.\n\n### Publishing\n\nNow, add more exercises and publish them as a Git repository.\n\nUsers just have to clone that repository and run `rustlings` in it to start working on your exercises (just like the official ones).\n\nOne difference to the official exercises is that the solution files will not be hidden until the user finishes an exercise.\nBut you can trust your users to not open the solution too early 😉\n\n### Sharing\n\nAfter publishing your community exercises, open an issue or a pull request in the [official Rustlings repository](https://github.com/rust-lang/rustlings) to add your project to the [list of community exercises](#list-of-community-exercises) 😃\n"
  },
  {
    "path": "website/content/setup/index.md",
    "content": "+++\ntitle = \"Setup\"\n+++\n\n<!-- toc -->\n\n## Installing Rust\n\nBefore installing Rustlings, you must have the **latest version of Rust** installed.\nVisit [www.rust-lang.org/tools/install](https://www.rust-lang.org/tools/install) for further instructions.\nThis will also install _Cargo_, Rust's package/project manager.\n\n> 🐧 If you are on **Linux**, make sure you have `gcc` installed (_for a linker_).\n>\n> Debian: `sudo apt install gcc`\\\n> Fedora: `sudo dnf install gcc`\n\n> 🍎 If you are on **MacOS**, make sure you have _Xcode and its developer tools_ installed: `xcode-select --install`\n\n## Installing Rustlings\n\nThe following command will download and compile Rustlings:\n\n```bash\ncargo install rustlings\n```\n\n{% details(summary=\"If the installation fails…\") %}\n\n- Make sure you have the latest Rust version by running `rustup update`\n- Try adding the `--locked` flag: `cargo install rustlings --locked`\n- Otherwise, please [report the issue](https://github.com/rust-lang/rustlings/issues/new)\n\n{% end %}\n\n## Initialization\n\nAfter installing Rustlings, run the following command to initialize the `rustlings/` directory:\n\n```bash\nrustlings init\n```\n\n{% details(summary=\"If the command <code>rustlings</code> can't be found…\") %}\n\nYou are probably using Linux and installed Rust using your package manager.\n\nCargo installs binaries to the directory `~/.cargo/bin`.\nSadly, package managers often don't add `~/.cargo/bin` to your `PATH` environment variable.\n\n- Either add `~/.cargo/bin` manually to `PATH`\n- Or uninstall Rust from the package manager and [install it using the official way with `rustup`](https://www.rust-lang.org/tools/install)\n\n{% end %}\n\nNow, go into the newly initialized directory and launch Rustlings for further instructions on getting started with the exercises:\n\n```bash\ncd rustlings/\nrustlings\n```\n\n## Working environment\n\n### Editor\n\nOur general recommendation is [VS Code](https://code.visualstudio.com/) with the [rust-analyzer plugin](https://marketplace.visualstudio.com/items?itemName=rust-lang.rust-analyzer).\nBut any editor that supports [rust-analyzer](https://rust-analyzer.github.io/) should be enough for working on the exercises.\n\n### Terminal\n\nWhile working with Rustlings, please use a modern terminal for the best user experience.\nThe default terminal on Linux and Mac should be sufficient.\nOn Windows, we recommend the [Windows Terminal](https://aka.ms/terminal).\n\n## Usage\n\nAfter being done with the setup, visit the [**usage**](@/usage/index.md) page for some info about using Rustlings 🚀\n"
  },
  {
    "path": "website/content/usage/index.md",
    "content": "+++\ntitle = \"Usage\"\n+++\n\n<!-- toc -->\n\n## Doing exercises\n\nThe exercises are sorted by topic and can be found in the subdirectory `exercises/<topic>`.\nFor every topic, there is an additional `README.md` file with some resources to get you started on the topic.\nWe highly recommend that you have a look at them before you start 📚️\n\nMost exercises contain an error that keeps them from compiling, and it's up to you to fix it!\nSome exercises contain tests that need to pass for the exercise to be done ✅\n\nSearch for `TODO` and `todo!()` to find out what you need to change.\nAsk for hints by entering `h` in the _watch mode_ 💡\n\n## Watch Mode\n\nAfter the [initialization](@/setup/index.md#initialization), Rustlings can be launched by simply running the command `rustlings`.\n\nThis will start the _watch mode_ which walks you through the exercises in a predefined order (what we think is best for newcomers).\nIt will rerun the current exercise automatically every time you change the exercise's file in the `exercises/` directory.\n\n{% details(summary=\"If detecting file changes in the <code>exercises/</code> directory fails…\") %}\n\nYou can add the **`--manual-run`** flag (`rustlings --manual-run`) to manually rerun the current exercise by entering `r` in the watch mode.\n\nPlease [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 a virtual machine (e.g. WSL).\n\n{% end %}\n\n## Exercise List\n\nIn the [watch mode](#watch-mode) (after launching `rustlings`), you can enter `l` to open the interactive exercise list.\n\nThe list allows you to…\n\n- See the status of all exercises (done or pending)\n- `c`: Continue at another exercise (temporarily skip some exercises or go back to a previous one)\n- `r`: Reset status and file of the selected exercise (you need to _reload/reopen_ its file in your editor afterwards)\n\nSee the footer of the list for all possible keys.\n\n## Questions?\n\nIf you need any help while doing the exercises and the builtin hints aren't helpful, feel free to ask in the [_Q&A_ discussions](https://github.com/rust-lang/rustlings/discussions/categories/q-a?discussions_q=) if your question isn't answered there 💡\n\n## Continuing On\n\nOnce you've completed Rustlings, put your new knowledge to good use!\nContinue practicing your Rust skills by building your own projects, contributing to Rustlings, or finding other open-source projects to contribute to.\n\n> If you want to create your own Rustlings exercises, visit the [**community exercises**](@/community-exercises/index.md) page 🏗️\n"
  },
  {
    "path": "website/input.css",
    "content": "@import 'tailwindcss';\n\n@layer base {\n  h1 {\n    @apply text-4xl mt-3 mb-3 font-bold;\n  }\n  h2 {\n    @apply text-3xl mt-4 mb-1.5 font-bold;\n  }\n  h3 {\n    @apply text-2xl mt-5 mb-1.5 font-bold;\n  }\n  h4 {\n    @apply text-xl mt-6 mb-1.5 font-bold;\n  }\n  p {\n    @apply mb-2;\n  }\n  a {\n    @apply text-[#FFC832] underline hover:decoration-orange-400 transition duration-300;\n  }\n  ul {\n    @apply mt-2 mb-3 ml-1 list-disc list-inside marker:text-sky-600;\n  }\n  ol {\n    @apply mt-2 mb-3 ml-1 list-decimal list-inside marker:text-sky-500;\n  }\n  li {\n    @apply my-0.5;\n  }\n  code {\n    @apply bg-white/10 px-1 pb-px pt-1 rounded-md;\n  }\n  pre code {\n    @apply bg-inherit p-0 text-inherit;\n  }\n  hr {\n    @apply my-5 rounded-full;\n  }\n  img {\n    @apply md:w-3/4 lg:w-3/5;\n  }\n  blockquote {\n    @apply px-3 pt-2 pb-px mb-4 mt-2 border-s-4 border-white/80 bg-white/7 rounded-sm;\n  }\n\n  pre {\n    @apply px-2 pt-2 pb-1.5 overflow-x-auto text-sm sm:text-base rounded-sm mt-2 mb-4 selection:bg-white/15;\n  }\n}\n"
  },
  {
    "path": "website/justfile",
    "content": "zola:\n\tzola serve --open\n\ntailwind:\n\tnpx @tailwindcss/cli -w -i input.css -o static/main.css\n"
  },
  {
    "path": "website/package.json",
    "content": "{\n  \"dependencies\": {\n    \"@tailwindcss/cli\": \"^4\"\n  }\n}\n"
  },
  {
    "path": "website/templates/404.html",
    "content": "{% extends \"base.html\" %}\n\n{% block content %}\n    <div class=\"flex flex-col mx-auto text-center\">\n        <h1>DON'T PANIC!</h1>\n        <h2>404: Page not found!</h2>\n\n        <img class=\"mx-auto max-h-[50vh]\"\n             src=\"{{ get_url(path='images/panic.svg') | safe }}\"\n             alt=\"\">\n\n        <a class=\"text-2xl font-bold\" href=\"{{ get_url(path='@/_index.md') }}\">Back to homepage</a>\n    </div>\n{% endblock %}\n"
  },
  {
    "path": "website/templates/anchor-link.html",
    "content": "<a class=\"text-white no-underline transition-none hover:underline\"\n   href=\"#{{ id }}\"></a>\n"
  },
  {
    "path": "website/templates/base.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n    <head>\n        <meta charset=\"UTF-8\">\n        <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n\n        {%- set timestamp = now(timestamp=true) -%}\n\n        {%- if page.title -%}\n            {% set_global title = page.title %}\n        {%- elif section.title -%}\n            {% set_global title = section.title %}\n        {%- else -%}\n            {% set_global title = config.title %}\n        {%- endif -%}\n\n        {%- if page.description -%}\n            {% set_global description = page.description %}\n        {%- elif section.description -%}\n            {% set_global description = section.description %}\n        {%- else -%}\n            {% set_global description = config.description %}\n        {%- endif -%}\n\n        {%- if page.permalink -%}\n            {% set_global permalink = page.permalink %}\n        {%- elif section.permalink -%}\n            {% set_global permalink = section.permalink %}\n        {%- endif %}\n\n        <title>{%- block title -%}{{- title -}}{%- endblock -%}</title>\n\n        <meta name=\"description\"\n              content=\"{%- block description -%}{{- description -}}{%- endblock -%}\">\n\n        <link rel=\"icon\"\n              type=\"image/x-icon\"\n              href=\"{{ get_url(path=config.extra.logo_path) | safe }}?v={{ timestamp }}\">\n\n        <link href=\"{{ get_url(path='main.css') | safe }}?v={{ timestamp }}\"\n              rel=\"stylesheet\">\n\n        <meta property=\"og:title\" content=\"{{ title }}\">\n        <meta property=\"og:description\" content=\"{{ description }}\">\n        <meta property=\"og:image\"\n              content=\"{{ get_url(path=config.extra.logo_path) | safe }}?v={{ timestamp }}\">\n        {% if permalink %}<meta property=\"og:url\" content=\"{{ permalink | safe }}\">{% endif %}\n    </head>\n\n    <body class=\"flex flex-col p-2 mx-auto min-h-screen text-lg text-white break-words lg:px-5 2xl:container bg-[#2A3439]\">\n        <header class=\"flex flex-col gap-x-4 items-center py-2 px-4 mb-1 rounded-sm sm:flex-row sm:rounded-full bg-black/30\">\n            <a class=\"transition duration-500 hover:scale-110\"\n               href=\"{{ get_url(path='@/_index.md') | safe }}\"\n               aria-hidden=\"true\">\n                <img class=\"w-12 h-12\"\n                     src=\"{{ get_url(path=config.extra.logo_path) | safe }}\"\n                     alt=\"\">\n            </a>\n\n            <nav class=\"flex flex-col gap-x-6 items-center font-bold sm:flex-row\">\n                {% for menu_item in config.extra.menu_items %}\n                    {%- if menu_item.url is starting_with(\"@\") -%}\n                        {% set_global menu_item_url = get_url(path=menu_item.url) %}\n                    {%- else -%}\n                        {% set_global menu_item_url = menu_item.url %}\n                    {%- endif %}\n\n                    <a class=\"p-1 no-underline\" href=\"{{ menu_item_url | safe }}\">{{ menu_item.name }}</a>\n                {% endfor %}\n            </nav>\n        </header>\n\n        <main class=\"leading-relaxed\">\n            {% block content %}{% endblock %}\n        </main>\n\n        <footer class=\"pt-2 pb-1 mt-auto text-sm text-center\">\n            <div class=\"inline-flex gap-x-1.5 items-center mx-auto mt-2\">\n                <img class=\"w-8 h-8\"\n                     src=\"{{ get_url(path='images/rust_logo.svg') | safe }}\"\n                     alt=\"\">\n                <div class=\"italic\">Rustlings is an official Rust project</div>\n            </div>\n\n            <nav class=\"flex flex-col gap-y-3 justify-around py-3 mt-3 rounded-sm sm:flex-row sm:rounded-full bg-black/30\">\n                {% for footer_item in config.extra.footer_items %}\n                    <a class=\"no-underline\" href=\"{{ footer_item.url | safe }}\">{{ footer_item.name }}</a>\n                {% endfor %}\n            </nav>\n        </footer>\n    </body>\n</html>\n"
  },
  {
    "path": "website/templates/index.html",
    "content": "{% extends \"base.html\" %}\n\n{% block content %}\n    <div class=\"m-3\">\n        <h1>Rustlings</h1>\n\n        {{ section.content | safe }}\n    </div>\n{% endblock %}\n"
  },
  {
    "path": "website/templates/page.html",
    "content": "{% extends \"base.html\" %}\n\n{% block content %}\n    <article>\n        <h1>{{ page.title }}</h1>\n\n        <div class=\"py-0.5 px-4 my-3 rounded-xl border-double border-s-4\">\n            <nav>\n                <ul class=\"ml-0 list-none\">\n                    {% for parent in page.toc %}\n                        {% if parent.level == 2 %}\n                            <li>\n                                {#- -#}\n                                <a href=\"{{ parent.permalink | safe }}\">{{ parent.title }}</a>\n                                {#- -#}\n                                {% if parent.children %}\n                                    <ul class=\"my-0 ml-5 list-none\">\n                                        {% for child in parent.children %}\n                                            {% if child.level == 3 %}\n                                                <li>\n                                                    {#- -#}\n                                                    <a class=\"text-base\" href=\"{{ child.permalink | safe }}\">{{ child.title }}</a>\n                                                    {#- -#}\n                                                </li>\n                                            {% endif %}\n                                        {% endfor %}\n                                    </ul>\n                                {% endif %}\n                                {#- -#}\n                            </li>\n                        {% endif %}\n                    {% endfor %}\n                </ul>\n            </nav>\n        </div>\n\n        {{ page.content | safe }}\n    </article>\n{% endblock %}\n"
  },
  {
    "path": "website/templates/shortcodes/details.html",
    "content": "<details>\n    <summary>\n        <strong>{{ summary | safe }}</strong> (<em>click to expand</em>)\n    </summary>\n\n    <blockquote class=\"pt-1 mx-0.5 mt-1 rounded-none border-dashed border-x-3 border-b-3\">\n        {{ body | markdown | safe }}\n    </blockquote>\n</details>\n"
  }
]