Merge pull request #1 from rust-lang/main

Merging Solutions
This commit is contained in:
Asad Ali 2023-05-31 15:20:28 +05:00 committed by GitHub
commit 82f62c9e80
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 236 additions and 53 deletions

View File

@ -2082,6 +2082,60 @@
"contributions": [ "contributions": [
"content" "content"
] ]
},
{
"login": "rb5014",
"name": "rb5014",
"avatar_url": "https://avatars.githubusercontent.com/u/105397317?v=4",
"profile": "https://github.com/rb5014",
"contributions": [
"content"
]
},
{
"login": "deedy5",
"name": "deedy5",
"avatar_url": "https://avatars.githubusercontent.com/u/65482418?v=4",
"profile": "https://github.com/deedy5",
"contributions": [
"content"
]
},
{
"login": "lionel-rowe",
"name": "lionel-rowe",
"avatar_url": "https://avatars.githubusercontent.com/u/26078826?v=4",
"profile": "https://github.com/lionel-rowe",
"contributions": [
"content"
]
},
{
"login": "Ben2917",
"name": "Ben",
"avatar_url": "https://avatars.githubusercontent.com/u/10279994?v=4",
"profile": "https://github.com/Ben2917",
"contributions": [
"content"
]
},
{
"login": "b1ue64",
"name": "b1ue64",
"avatar_url": "https://avatars.githubusercontent.com/u/77976308?v=4",
"profile": "https://github.com/b1ue64",
"contributions": [
"content"
]
},
{
"login": "lazywalker",
"name": "lazywalker",
"avatar_url": "https://avatars.githubusercontent.com/u/53956?v=4",
"profile": "https://github.com/lazywalker",
"contributions": [
"content"
]
} }
], ],
"contributorsPerLine": 8, "contributorsPerLine": 8,

4
.gitignore vendored
View File

@ -11,3 +11,7 @@ rust-project.json
!.vscode/extensions.json !.vscode/extensions.json
*.iml *.iml
*.o *.o
public/
# Local Netlify folder
.netlify

View File

@ -295,6 +295,14 @@ authors.
<td align="center" valign="top" width="12.5%"><a href="http://esotuvaka.github.io"><img src="https://avatars.githubusercontent.com/u/104941850?v=4?s=100" width="100px;" alt="Eric"/><br /><sub><b>Eric</b></sub></a><br /><a href="#content-esotuvaka" title="Content">🖋</a></td> <td align="center" valign="top" width="12.5%"><a href="http://esotuvaka.github.io"><img src="https://avatars.githubusercontent.com/u/104941850?v=4?s=100" width="100px;" alt="Eric"/><br /><sub><b>Eric</b></sub></a><br /><a href="#content-esotuvaka" title="Content">🖋</a></td>
<td align="center" valign="top" width="12.5%"><a href="https://github.com/az0977776"><img src="https://avatars.githubusercontent.com/u/9172038?v=4?s=100" width="100px;" alt="Aaron Wang"/><br /><sub><b>Aaron Wang</b></sub></a><br /><a href="#content-az0977776" title="Content">🖋</a></td> <td align="center" valign="top" width="12.5%"><a href="https://github.com/az0977776"><img src="https://avatars.githubusercontent.com/u/9172038?v=4?s=100" width="100px;" alt="Aaron Wang"/><br /><sub><b>Aaron Wang</b></sub></a><br /><a href="#content-az0977776" title="Content">🖋</a></td>
<td align="center" valign="top" width="12.5%"><a href="https://github.com/nmay231"><img src="https://avatars.githubusercontent.com/u/35386821?v=4?s=100" width="100px;" alt="Noah"/><br /><sub><b>Noah</b></sub></a><br /><a href="#content-nmay231" title="Content">🖋</a></td> <td align="center" valign="top" width="12.5%"><a href="https://github.com/nmay231"><img src="https://avatars.githubusercontent.com/u/35386821?v=4?s=100" width="100px;" alt="Noah"/><br /><sub><b>Noah</b></sub></a><br /><a href="#content-nmay231" title="Content">🖋</a></td>
<td align="center" valign="top" width="12.5%"><a href="https://github.com/rb5014"><img src="https://avatars.githubusercontent.com/u/105397317?v=4?s=100" width="100px;" alt="rb5014"/><br /><sub><b>rb5014</b></sub></a><br /><a href="#content-rb5014" title="Content">🖋</a></td>
<td align="center" valign="top" width="12.5%"><a href="https://github.com/deedy5"><img src="https://avatars.githubusercontent.com/u/65482418?v=4?s=100" width="100px;" alt="deedy5"/><br /><sub><b>deedy5</b></sub></a><br /><a href="#content-deedy5" title="Content">🖋</a></td>
<td align="center" valign="top" width="12.5%"><a href="https://github.com/lionel-rowe"><img src="https://avatars.githubusercontent.com/u/26078826?v=4?s=100" width="100px;" alt="lionel-rowe"/><br /><sub><b>lionel-rowe</b></sub></a><br /><a href="#content-lionel-rowe" title="Content">🖋</a></td>
<td align="center" valign="top" width="12.5%"><a href="https://github.com/Ben2917"><img src="https://avatars.githubusercontent.com/u/10279994?v=4?s=100" width="100px;" alt="Ben"/><br /><sub><b>Ben</b></sub></a><br /><a href="#content-Ben2917" title="Content">🖋</a></td>
</tr>
<tr>
<td align="center" valign="top" width="12.5%"><a href="https://github.com/b1ue64"><img src="https://avatars.githubusercontent.com/u/77976308?v=4?s=100" width="100px;" alt="b1ue64"/><br /><sub><b>b1ue64</b></sub></a><br /><a href="#content-b1ue64" title="Content">🖋</a></td>
<td align="center" valign="top" width="12.5%"><a href="https://github.com/lazywalker"><img src="https://avatars.githubusercontent.com/u/53956?v=4?s=100" width="100px;" alt="lazywalker"/><br /><sub><b>lazywalker</b></sub></a><br /><a href="#content-lazywalker" title="Content">🖋</a></td>
</tr> </tr>
</tbody> </tbody>
</table> </table>

View File

@ -1,3 +1,45 @@
<a name="5.5.1"></a>
## 5.5.1 (2023-05-17)
#### Fixed
- Reverted `rust-project.json` path generation due to an upstream `rust-analyzer` fix.
<a name="5.5.0"></a>
## 5.5.0 (2023-05-17)
#### Added
- `strings2`: Added a reference to the book chapter for reference conversion
- `lifetimes`: Added a link to the lifetimekata project
- Added a new `tests4` exercises, which teaches about testing for panics
- Added a `!` prefix command to watch mode that runs an external command
- Added a `--success-hints` option to watch mode that shows hints on exercise success
#### Changed
- `vecs2`: Renamed iterator variable bindings for clarify
- `lifetimes`: Changed order of book references
- `hashmaps2`: Clarified instructions in the todo block
- Moved lifetime exercises before test exercises (via the recommended book ordering)
- `options2`: Improved tests for layering options
- `modules2`: Added more information to the hint
#### Fixed
- `errors2`: Corrected a comment wording
- `iterators2`: Fixed a spelling mistake in the hint text
- `variables`: Wrapped the mut keyword with backticks for readability
- `move_semantics2`: Removed references to line numbers
- `cow1`: Clarified the `owned_no_mutation` comments
- `options3`: Changed exercise to panic when no match is found
- `rustlings lsp` now generates absolute paths, which should fix VSCode `rust-analyzer` usage on Windows
#### Housekeeping
- Added a markdown linter to run on GitHub actions
- Split quick installation section into two code blocks
<a name="5.4.1"></a> <a name="5.4.1"></a>
## 5.4.1 (2023-03-10) ## 5.4.1 (2023-03-10)

2
Cargo.lock generated
View File

@ -441,7 +441,7 @@ checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1"
[[package]] [[package]]
name = "rustlings" name = "rustlings"
version = "5.4.1" version = "5.5.1"
dependencies = [ dependencies = [
"argh", "argh",
"assert_cmd", "assert_cmd",

View File

@ -1,6 +1,7 @@
[package] [package]
name = "rustlings" name = "rustlings"
version = "5.4.1" description = "Small exercises to get you used to reading and writing Rust code!"
version = "5.5.1"
authors = [ authors = [
"Liv <mokou@fastmail.com>", "Liv <mokou@fastmail.com>",
"Carol (Nichols || Goulding) <carol.nichols@gmail.com>", "Carol (Nichols || Goulding) <carol.nichols@gmail.com>",

View File

@ -1,5 +1,9 @@
<div class="oranda-hide">
# rustlings 🦀❤️ # rustlings 🦀❤️
</div>
Greetings and welcome to `rustlings`. This project contains small exercises to get you used to reading and writing Rust code. This includes reading and responding to compiler messages! Greetings and welcome to `rustlings`. This project contains small exercises to get you used to reading and writing Rust code. This includes reading and responding to compiler messages!
_...looking for the old, web-based version of Rustlings? Try [here](https://github.com/rust-lang/rustlings/tree/rustlings-1)_ _...looking for the old, web-based version of Rustlings? Try [here](https://github.com/rust-lang/rustlings/tree/rustlings-1)_
@ -36,8 +40,8 @@ This will install Rustlings and give you access to the `rustlings` command. Run
Basically: Clone the repository at the latest tag, finally run `nix develop` or `nix-shell`. Basically: Clone the repository at the latest tag, finally run `nix develop` or `nix-shell`.
```bash ```bash
# find out the latest version at https://github.com/rust-lang/rustlings/releases/latest (on edit 5.4.1) # find out the latest version at https://github.com/rust-lang/rustlings/releases/latest (on edit 5.5.1)
git clone -b 5.4.1 --depth 1 https://github.com/rust-lang/rustlings git clone -b 5.5.1 --depth 1 https://github.com/rust-lang/rustlings
cd rustlings cd rustlings
# if nix version > 2.3 # if nix version > 2.3
nix develop nix develop
@ -74,8 +78,8 @@ If you get a permission denied message, you might have to exclude the directory
Basically: Clone the repository at the latest tag, run `cargo install --path .`. Basically: Clone the repository at the latest tag, run `cargo install --path .`.
```bash ```bash
# find out the latest version at https://github.com/rust-lang/rustlings/releases/latest (on edit 5.4.1) # find out the latest version at https://github.com/rust-lang/rustlings/releases/latest (on edit 5.5.1)
git clone -b 5.4.1 --depth 1 https://github.com/rust-lang/rustlings git clone -b 5.5.1 --depth 1 https://github.com/rust-lang/rustlings
cd rustlings cd rustlings
cargo install --force --path . cargo install --force --path .
``` ```

View File

@ -65,12 +65,27 @@ mod tests {
} }
#[test] #[test]
fn count_equals_for() { fn count_some() {
let map = get_map(); let map = get_map();
assert_eq!( assert_eq!(1, count_iterator(&map, Progress::Some));
count_for(&map, Progress::Complete), }
count_iterator(&map, Progress::Complete)
); #[test]
fn count_none() {
let map = get_map();
assert_eq!(2, count_iterator(&map, Progress::None));
}
#[test]
fn count_complete_equals_for() {
let map = get_map();
let progress_states = vec![Progress::Complete, Progress::Some, Progress::None];
for progress_state in progress_states {
assert_eq!(
count_for(&map, progress_state),
count_iterator(&map, progress_state)
);
}
} }
#[test] #[test]
@ -83,12 +98,28 @@ mod tests {
} }
#[test] #[test]
fn count_collection_equals_for() { fn count_collection_some() {
let collection = get_vec_map(); let collection = get_vec_map();
assert_eq!( assert_eq!(1, count_collection_iterator(&collection, Progress::Some));
count_collection_for(&collection, Progress::Complete), }
count_collection_iterator(&collection, Progress::Complete)
); #[test]
fn count_collection_none() {
let collection = get_vec_map();
assert_eq!(4, count_collection_iterator(&collection, Progress::None));
}
#[test]
fn count_collection_equals_for() {
let progress_states = vec![Progress::Complete, Progress::Some, Progress::None];
let collection = get_vec_map();
for progress_state in progress_states {
assert_eq!(
count_collection_for(&collection, progress_state),
count_collection_iterator(&collection, progress_state)
);
}
} }
fn get_map() -> HashMap<String, Progress> { fn get_map() -> HashMap<String, Progress> {

View File

@ -18,17 +18,22 @@ mod tests {
#[test] #[test]
fn layered_option() { fn layered_option() {
let mut range = 10; let range = 10;
let mut optional_integers: Vec<Option<i8>> = Vec::new(); let mut optional_integers: Vec<Option<i8>> = vec![None];
for i in 0..(range + 1) {
for i in 1..(range + 1) {
optional_integers.push(Some(i)); optional_integers.push(Some(i));
} }
let mut cursor = range;
// TODO: make this a while let statement - remember that vector.pop also adds another layer of Option<T> // TODO: make this a while let statement - remember that vector.pop also adds another layer of Option<T>
// You can stack `Option<T>`'s into while let and if let // You can stack `Option<T>`s into while let and if let
integer = optional_integers.pop() { integer = optional_integers.pop() {
assert_eq!(integer, range); assert_eq!(integer, cursor);
range -= 1; cursor -= 1;
} }
assert_eq!(cursor, 0);
} }
} }

View File

@ -22,7 +22,7 @@
rustlings = rustlings =
pkgs.rustPlatform.buildRustPackage { pkgs.rustPlatform.buildRustPackage {
name = "rustlings"; name = "rustlings";
version = "5.4.1"; version = "5.5.1";
buildInputs = cargoBuildInputs; buildInputs = cargoBuildInputs;

View File

@ -478,7 +478,8 @@ hint = """
The delicious_snacks module is trying to present an external interface that is The delicious_snacks module is trying to present an external interface that is
different than its internal structure (the `fruits` and `veggies` modules and different than its internal structure (the `fruits` and `veggies` modules and
associated constants). Complete the `use` statements to fit the uses in main and associated constants). Complete the `use` statements to fit the uses in main and
find the one keyword missing for both constants.""" find the one keyword missing for both constants.
Learn more at https://doc.rust-lang.org/book/ch07-04-bringing-paths-into-scope-with-the-use-keyword.html#re-exporting-names-with-pub-use"""
[[exercises]] [[exercises]]
name = "modules3" name = "modules3"
@ -896,9 +897,6 @@ hint = """
The documentation for the std::iter::Iterator trait contains numerous methods The documentation for the std::iter::Iterator trait contains numerous methods
that would be helpful here. that would be helpful here.
Return 0 from count_collection_iterator to make the code compile in order to
test count_iterator.
The collection variable in count_collection_iterator is a slice of HashMaps. It The collection variable in count_collection_iterator is a slice of HashMaps. It
needs to be converted into an iterator in order to use the iterator methods. needs to be converted into an iterator in order to use the iterator methods.

10
oranda.json Normal file
View File

@ -0,0 +1,10 @@
{
"homepage": "https://rustlings.cool",
"repository": "https://github.com/rust-lang/rustlings",
"analytics": {
"plausible": {
"domain": "rustlings.cool"
}
},
"changelog": false
}

View File

@ -26,7 +26,7 @@ mod run;
mod verify; mod verify;
// In sync with crate version // In sync with crate version
const VERSION: &str = "5.4.1"; const VERSION: &str = "5.5.1";
#[derive(FromArgs, PartialEq, Debug)] #[derive(FromArgs, PartialEq, Debug)]
/// Rustlings is a collection of small exercises to get you used to writing and reading Rust code /// Rustlings is a collection of small exercises to get you used to writing and reading Rust code
@ -61,7 +61,11 @@ struct VerifyArgs {}
#[derive(FromArgs, PartialEq, Debug)] #[derive(FromArgs, PartialEq, Debug)]
#[argh(subcommand, name = "watch")] #[argh(subcommand, name = "watch")]
/// Reruns `verify` when files were edited /// Reruns `verify` when files were edited
struct WatchArgs {} struct WatchArgs {
/// show hints on success
#[argh(switch)]
success_hints: bool,
}
#[derive(FromArgs, PartialEq, Debug)] #[derive(FromArgs, PartialEq, Debug)]
#[argh(subcommand, name = "run")] #[argh(subcommand, name = "run")]
@ -229,7 +233,7 @@ fn main() {
} }
Subcommands::Verify(_subargs) => { Subcommands::Verify(_subargs) => {
verify(&exercises, (0, exercises.len()), verbose) verify(&exercises, (0, exercises.len()), verbose, false)
.unwrap_or_else(|_| std::process::exit(1)); .unwrap_or_else(|_| std::process::exit(1));
} }
@ -252,7 +256,7 @@ fn main() {
} }
} }
Subcommands::Watch(_subargs) => match watch(&exercises, verbose) { Subcommands::Watch(_subargs) => match watch(&exercises, verbose, _subargs.success_hints) {
Err(e) => { Err(e) => {
println!( println!(
"Error: Could not watch your progress. Error message was {:?}.", "Error: Could not watch your progress. Error message was {:?}.",
@ -348,7 +352,11 @@ enum WatchStatus {
Unfinished, Unfinished,
} }
fn watch(exercises: &[Exercise], verbose: bool) -> notify::Result<WatchStatus> { fn watch(
exercises: &[Exercise],
verbose: bool,
success_hints: bool,
) -> notify::Result<WatchStatus> {
/* Clears the terminal with an ANSI escape code. /* Clears the terminal with an ANSI escape code.
Works in UNIX and newer Windows terminals. */ Works in UNIX and newer Windows terminals. */
fn clear_screen() { fn clear_screen() {
@ -364,7 +372,12 @@ fn watch(exercises: &[Exercise], verbose: bool) -> notify::Result<WatchStatus> {
clear_screen(); clear_screen();
let to_owned_hint = |t: &Exercise| t.hint.to_owned(); let to_owned_hint = |t: &Exercise| t.hint.to_owned();
let failed_exercise_hint = match verify(exercises.iter(), (0, exercises.len()), verbose) { let failed_exercise_hint = match verify(
exercises.iter(),
(0, exercises.len()),
verbose,
success_hints,
) {
Ok(_) => return Ok(WatchStatus::Finished), Ok(_) => return Ok(WatchStatus::Finished),
Err(exercise) => Arc::new(Mutex::new(Some(to_owned_hint(exercise)))), Err(exercise) => Arc::new(Mutex::new(Some(to_owned_hint(exercise)))),
}; };
@ -386,7 +399,12 @@ fn watch(exercises: &[Exercise], verbose: bool) -> notify::Result<WatchStatus> {
); );
let num_done = exercises.iter().filter(|e| e.looks_done()).count(); let num_done = exercises.iter().filter(|e| e.looks_done()).count();
clear_screen(); clear_screen();
match verify(pending_exercises, (num_done, exercises.len()), verbose) { match verify(
pending_exercises,
(num_done, exercises.len()),
verbose,
success_hints,
) {
Ok(_) => return Ok(WatchStatus::Finished), Ok(_) => return Ok(WatchStatus::Finished),
Err(exercise) => { Err(exercise) => {
let mut failed_exercise_hint = failed_exercise_hint.lock().unwrap(); let mut failed_exercise_hint = failed_exercise_hint.lock().unwrap();

View File

@ -2,6 +2,7 @@ use glob::glob;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::env; use std::env;
use std::error::Error; use std::error::Error;
use std::path::PathBuf;
use std::process::Command; use std::process::Command;
/// Contains the structure of resulting rust-project.json file /// Contains the structure of resulting rust-project.json file
@ -38,11 +39,11 @@ impl RustAnalyzerProject {
} }
/// If path contains .rs extension, add a crate to `rust-project.json` /// If path contains .rs extension, add a crate to `rust-project.json`
fn path_to_json(&mut self, path: String) { fn path_to_json(&mut self, path: PathBuf) -> Result<(), Box<dyn Error>> {
if let Some((_, ext)) = path.split_once('.') { if let Some(ext) = path.extension() {
if ext == "rs" { if ext == "rs" {
self.crates.push(Crate { self.crates.push(Crate {
root_module: path, root_module: path.display().to_string(),
edition: "2021".to_string(), edition: "2021".to_string(),
deps: Vec::new(), deps: Vec::new(),
// This allows rust_analyzer to work inside #[test] blocks // This allows rust_analyzer to work inside #[test] blocks
@ -50,15 +51,16 @@ impl RustAnalyzerProject {
}) })
} }
} }
Ok(())
} }
/// Parse the exercises folder for .rs files, any matches will create /// Parse the exercises folder for .rs files, any matches will create
/// a new `crate` in rust-project.json which allows rust-analyzer to /// a new `crate` in rust-project.json which allows rust-analyzer to
/// treat it like a normal binary /// treat it like a normal binary
pub fn exercises_to_json(&mut self) -> Result<(), Box<dyn Error>> { pub fn exercises_to_json(&mut self) -> Result<(), Box<dyn Error>> {
for e in glob("./exercises/**/*")? { for path in glob("./exercises/**/*")? {
let path = e?.to_string_lossy().to_string(); self.path_to_json(path?)?;
self.path_to_json(path);
} }
Ok(()) Ok(())
} }

View File

@ -12,6 +12,7 @@ pub fn verify<'a>(
exercises: impl IntoIterator<Item = &'a Exercise>, exercises: impl IntoIterator<Item = &'a Exercise>,
progress: (usize, usize), progress: (usize, usize),
verbose: bool, verbose: bool,
success_hints: bool,
) -> Result<(), &'a Exercise> { ) -> Result<(), &'a Exercise> {
let (num_done, total) = progress; let (num_done, total) = progress;
let bar = ProgressBar::new(total as u64); let bar = ProgressBar::new(total as u64);
@ -25,9 +26,9 @@ pub fn verify<'a>(
for exercise in exercises { for exercise in exercises {
let compile_result = match exercise.mode { let compile_result = match exercise.mode {
Mode::Test => compile_and_test(exercise, RunMode::Interactive, verbose), Mode::Test => compile_and_test(exercise, RunMode::Interactive, verbose, success_hints),
Mode::Compile => compile_and_run_interactively(exercise), Mode::Compile => compile_and_run_interactively(exercise, success_hints),
Mode::Clippy => compile_only(exercise), Mode::Clippy => compile_only(exercise, success_hints),
}; };
if !compile_result.unwrap_or(false) { if !compile_result.unwrap_or(false) {
return Err(exercise); return Err(exercise);
@ -46,12 +47,12 @@ enum RunMode {
// Compile and run the resulting test harness of the given Exercise // Compile and run the resulting test harness of the given Exercise
pub fn test(exercise: &Exercise, verbose: bool) -> Result<(), ()> { pub fn test(exercise: &Exercise, verbose: bool) -> Result<(), ()> {
compile_and_test(exercise, RunMode::NonInteractive, verbose)?; compile_and_test(exercise, RunMode::NonInteractive, verbose, false)?;
Ok(()) Ok(())
} }
// Invoke the rust compiler without running the resulting binary // Invoke the rust compiler without running the resulting binary
fn compile_only(exercise: &Exercise) -> Result<bool, ()> { fn compile_only(exercise: &Exercise, success_hints: bool) -> Result<bool, ()> {
let progress_bar = ProgressBar::new_spinner(); let progress_bar = ProgressBar::new_spinner();
progress_bar.set_message(format!("Compiling {exercise}...")); progress_bar.set_message(format!("Compiling {exercise}..."));
progress_bar.enable_steady_tick(100); progress_bar.enable_steady_tick(100);
@ -59,11 +60,11 @@ fn compile_only(exercise: &Exercise) -> Result<bool, ()> {
let _ = compile(exercise, &progress_bar)?; let _ = compile(exercise, &progress_bar)?;
progress_bar.finish_and_clear(); progress_bar.finish_and_clear();
Ok(prompt_for_completion(exercise, None)) Ok(prompt_for_completion(exercise, None, success_hints))
} }
// Compile the given Exercise and run the resulting binary in an interactive mode // Compile the given Exercise and run the resulting binary in an interactive mode
fn compile_and_run_interactively(exercise: &Exercise) -> Result<bool, ()> { fn compile_and_run_interactively(exercise: &Exercise, success_hints: bool) -> Result<bool, ()> {
let progress_bar = ProgressBar::new_spinner(); let progress_bar = ProgressBar::new_spinner();
progress_bar.set_message(format!("Compiling {exercise}...")); progress_bar.set_message(format!("Compiling {exercise}..."));
progress_bar.enable_steady_tick(100); progress_bar.enable_steady_tick(100);
@ -84,12 +85,12 @@ fn compile_and_run_interactively(exercise: &Exercise) -> Result<bool, ()> {
} }
}; };
Ok(prompt_for_completion(exercise, Some(output.stdout))) Ok(prompt_for_completion(exercise, Some(output.stdout), success_hints))
} }
// Compile the given Exercise as a test harness and display // Compile the given Exercise as a test harness and display
// the output if verbose is set to true // the output if verbose is set to true
fn compile_and_test(exercise: &Exercise, run_mode: RunMode, verbose: bool) -> Result<bool, ()> { fn compile_and_test(exercise: &Exercise, run_mode: RunMode, verbose: bool, success_hints: bool) -> Result<bool, ()> {
let progress_bar = ProgressBar::new_spinner(); let progress_bar = ProgressBar::new_spinner();
progress_bar.set_message(format!("Testing {exercise}...")); progress_bar.set_message(format!("Testing {exercise}..."));
progress_bar.enable_steady_tick(100); progress_bar.enable_steady_tick(100);
@ -104,7 +105,7 @@ fn compile_and_test(exercise: &Exercise, run_mode: RunMode, verbose: bool) -> Re
println!("{}", output.stdout); println!("{}", output.stdout);
} }
if let RunMode::Interactive = run_mode { if let RunMode::Interactive = run_mode {
Ok(prompt_for_completion(exercise, None)) Ok(prompt_for_completion(exercise, None, success_hints))
} else { } else {
Ok(true) Ok(true)
} }
@ -142,12 +143,11 @@ fn compile<'a, 'b>(
} }
} }
fn prompt_for_completion(exercise: &Exercise, prompt_output: Option<String>) -> bool { fn prompt_for_completion(exercise: &Exercise, prompt_output: Option<String>, success_hints: bool) -> bool {
let context = match exercise.state() { let context = match exercise.state() {
State::Done => return true, State::Done => return true,
State::Pending(context) => context, State::Pending(context) => context,
}; };
match exercise.mode { match exercise.mode {
Mode::Compile => success!("Successfully ran {}!", exercise), Mode::Compile => success!("Successfully ran {}!", exercise),
Mode::Test => success!("Successfully tested {}!", exercise), Mode::Test => success!("Successfully tested {}!", exercise),
@ -167,7 +167,6 @@ fn prompt_for_completion(exercise: &Exercise, prompt_output: Option<String>) ->
Mode::Test => "The code is compiling, and the tests pass!", Mode::Test => "The code is compiling, and the tests pass!",
Mode::Clippy => clippy_success_msg, Mode::Clippy => clippy_success_msg,
}; };
println!(); println!();
if no_emoji { if no_emoji {
println!("~*~ {success_msg} ~*~") println!("~*~ {success_msg} ~*~")
@ -183,6 +182,13 @@ fn prompt_for_completion(exercise: &Exercise, prompt_output: Option<String>) ->
println!("{}", separator()); println!("{}", separator());
println!(); println!();
} }
if success_hints {
println!("Hints:");
println!("{}", separator());
println!("{}", exercise.hint);
println!("{}", separator());
println!();
}
println!("You can keep working on this exercise,"); println!("You can keep working on this exercise,");
println!( println!(