summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authormo8it <mo8it@proton.me>2024-04-11 02:51:02 +0200
committermo8it <mo8it@proton.me>2024-04-11 02:51:02 +0200
commitfa1f239a702eb2c0b7e0115e986481156961bbc8 (patch)
tree08ac7c8638546f80ac650474dfe4126103a15e54
parent4bb6bda9f6416e30233342e73fc9a8486faa3f98 (diff)
Remove "I AM NOT DONE" and the verify mode and add AppState
-rw-r--r--Cargo.lock1
-rw-r--r--Cargo.toml1
-rw-r--r--README.md8
-rw-r--r--exercises/00_intro/intro1.rs4
-rw-r--r--exercises/00_intro/intro2.rs2
-rw-r--r--exercises/01_variables/variables1.rs2
-rw-r--r--exercises/01_variables/variables2.rs2
-rw-r--r--exercises/01_variables/variables3.rs2
-rw-r--r--exercises/01_variables/variables4.rs2
-rw-r--r--exercises/01_variables/variables5.rs2
-rw-r--r--exercises/01_variables/variables6.rs2
-rw-r--r--exercises/02_functions/functions1.rs2
-rw-r--r--exercises/02_functions/functions2.rs2
-rw-r--r--exercises/02_functions/functions3.rs2
-rw-r--r--exercises/02_functions/functions4.rs2
-rw-r--r--exercises/02_functions/functions5.rs2
-rw-r--r--exercises/03_if/if1.rs2
-rw-r--r--exercises/03_if/if2.rs2
-rw-r--r--exercises/03_if/if3.rs2
-rw-r--r--exercises/04_primitive_types/primitive_types1.rs2
-rw-r--r--exercises/04_primitive_types/primitive_types2.rs2
-rw-r--r--exercises/04_primitive_types/primitive_types3.rs2
-rw-r--r--exercises/04_primitive_types/primitive_types4.rs2
-rw-r--r--exercises/04_primitive_types/primitive_types5.rs2
-rw-r--r--exercises/04_primitive_types/primitive_types6.rs2
-rw-r--r--exercises/05_vecs/vecs1.rs2
-rw-r--r--exercises/05_vecs/vecs2.rs2
-rw-r--r--exercises/06_move_semantics/move_semantics1.rs2
-rw-r--r--exercises/06_move_semantics/move_semantics2.rs2
-rw-r--r--exercises/06_move_semantics/move_semantics3.rs2
-rw-r--r--exercises/06_move_semantics/move_semantics4.rs2
-rw-r--r--exercises/06_move_semantics/move_semantics5.rs2
-rw-r--r--exercises/06_move_semantics/move_semantics6.rs2
-rw-r--r--exercises/07_structs/structs1.rs2
-rw-r--r--exercises/07_structs/structs2.rs2
-rw-r--r--exercises/07_structs/structs3.rs2
-rw-r--r--exercises/08_enums/enums1.rs2
-rw-r--r--exercises/08_enums/enums2.rs2
-rw-r--r--exercises/08_enums/enums3.rs2
-rw-r--r--exercises/09_strings/strings1.rs2
-rw-r--r--exercises/09_strings/strings2.rs2
-rw-r--r--exercises/09_strings/strings3.rs2
-rw-r--r--exercises/09_strings/strings4.rs2
-rw-r--r--exercises/10_modules/modules1.rs2
-rw-r--r--exercises/10_modules/modules2.rs2
-rw-r--r--exercises/10_modules/modules3.rs2
-rw-r--r--exercises/11_hashmaps/hashmaps1.rs2
-rw-r--r--exercises/11_hashmaps/hashmaps2.rs2
-rw-r--r--exercises/11_hashmaps/hashmaps3.rs2
-rw-r--r--exercises/12_options/options1.rs2
-rw-r--r--exercises/12_options/options2.rs2
-rw-r--r--exercises/12_options/options3.rs2
-rw-r--r--exercises/13_error_handling/errors1.rs2
-rw-r--r--exercises/13_error_handling/errors2.rs2
-rw-r--r--exercises/13_error_handling/errors3.rs2
-rw-r--r--exercises/13_error_handling/errors4.rs2
-rw-r--r--exercises/13_error_handling/errors5.rs2
-rw-r--r--exercises/13_error_handling/errors6.rs2
-rw-r--r--exercises/14_generics/generics1.rs2
-rw-r--r--exercises/14_generics/generics2.rs2
-rw-r--r--exercises/15_traits/traits1.rs2
-rw-r--r--exercises/15_traits/traits2.rs2
-rw-r--r--exercises/15_traits/traits3.rs2
-rw-r--r--exercises/15_traits/traits4.rs2
-rw-r--r--exercises/15_traits/traits5.rs2
-rw-r--r--exercises/16_lifetimes/lifetimes1.rs2
-rw-r--r--exercises/16_lifetimes/lifetimes2.rs2
-rw-r--r--exercises/16_lifetimes/lifetimes3.rs2
-rw-r--r--exercises/17_tests/tests1.rs2
-rw-r--r--exercises/17_tests/tests2.rs2
-rw-r--r--exercises/17_tests/tests3.rs2
-rw-r--r--exercises/17_tests/tests4.rs2
-rw-r--r--exercises/18_iterators/iterators1.rs2
-rw-r--r--exercises/18_iterators/iterators2.rs2
-rw-r--r--exercises/18_iterators/iterators3.rs2
-rw-r--r--exercises/18_iterators/iterators4.rs2
-rw-r--r--exercises/18_iterators/iterators5.rs2
-rw-r--r--exercises/19_smart_pointers/arc1.rs2
-rw-r--r--exercises/19_smart_pointers/box1.rs2
-rw-r--r--exercises/19_smart_pointers/cow1.rs2
-rw-r--r--exercises/19_smart_pointers/rc1.rs2
-rw-r--r--exercises/20_threads/threads1.rs2
-rw-r--r--exercises/20_threads/threads2.rs2
-rw-r--r--exercises/20_threads/threads3.rs2
-rw-r--r--exercises/21_macros/macros1.rs2
-rw-r--r--exercises/21_macros/macros2.rs2
-rw-r--r--exercises/21_macros/macros3.rs2
-rw-r--r--exercises/21_macros/macros4.rs2
-rw-r--r--exercises/22_clippy/clippy1.rs2
-rw-r--r--exercises/22_clippy/clippy2.rs2
-rw-r--r--exercises/22_clippy/clippy3.rs2
-rw-r--r--exercises/23_conversions/as_ref_mut.rs2
-rw-r--r--exercises/23_conversions/from_into.rs2
-rw-r--r--exercises/23_conversions/from_str.rs2
-rw-r--r--exercises/23_conversions/try_from_into.rs2
-rw-r--r--exercises/23_conversions/using_as.rs2
-rw-r--r--exercises/quiz1.rs2
-rw-r--r--exercises/quiz2.rs2
-rw-r--r--exercises/quiz3.rs2
-rw-r--r--info.toml7
-rw-r--r--src/app_state.rs185
-rw-r--r--src/exercise.rs206
-rw-r--r--src/list.rs21
-rw-r--r--src/list/state.rs71
-rw-r--r--src/main.rs67
-rw-r--r--src/run.rs23
-rw-r--r--src/state_file.rs68
-rw-r--r--src/verify.rs85
-rw-r--r--src/watch.rs10
-rw-r--r--src/watch/state.rs96
-rw-r--r--tests/fixture/state/exercises/pending_exercise.rs2
-rw-r--r--tests/fixture/state/exercises/pending_test_exercise.rs2
-rw-r--r--tests/integration_tests.rs28
113 files changed, 306 insertions, 769 deletions
diff --git a/Cargo.lock b/Cargo.lock
index ee46943..aeb6c61 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -699,7 +699,6 @@ dependencies = [
"serde_json",
"toml_edit",
"which",
- "winnow",
]
[[package]]
diff --git a/Cargo.toml b/Cargo.toml
index da09ba1..435dfd4 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -44,7 +44,6 @@ serde_json = "1.0.115"
serde.workspace = true
toml_edit.workspace = true
which = "6.0.1"
-winnow = "0.6.5"
[dev-dependencies]
assert_cmd = "2.0.14"
diff --git a/README.md b/README.md
index 6b9c983..fd76fdf 100644
--- a/README.md
+++ b/README.md
@@ -101,13 +101,7 @@ The task is simple. Most exercises contain an error that keeps them from compili
rustlings watch
```
-This will try to verify the completion of every exercise in a predetermined order (what we think is best for newcomers). It will also rerun automatically every time you change a file in the `exercises/` directory. If you want to only run it once, you can use:
-
-```bash
-rustlings verify
-```
-
-This will do the same as watch, but it'll quit after running.
+This will try to verify the completion of every exercise in a predetermined order (what we think is best for newcomers). It will also rerun automatically every time you change a file in the `exercises/` directory.
In case you want to go by your own order, or want to only verify a single exercise, you can run:
diff --git a/exercises/00_intro/intro1.rs b/exercises/00_intro/intro1.rs
index 5dd18b4..aa505a1 100644
--- a/exercises/00_intro/intro1.rs
+++ b/exercises/00_intro/intro1.rs
@@ -1,6 +1,6 @@
// intro1.rs
//
-// About this `I AM NOT DONE` thing:
+// TODO: Update comment
// We sometimes encourage you to keep trying things on a given exercise, even
// after you already figured it out. If you got everything working and feel
// ready for the next exercise, remove the `I AM NOT DONE` comment below.
@@ -13,8 +13,6 @@
// Execute `rustlings hint intro1` or use the `hint` watch subcommand for a
// hint.
-// I AM NOT DONE
-
fn main() {
println!("Hello and");
println!(r#" welcome to... "#);
diff --git a/exercises/00_intro/intro2.rs b/exercises/00_intro/intro2.rs
index a28ad3d..84e0d75 100644
--- a/exercises/00_intro/intro2.rs
+++ b/exercises/00_intro/intro2.rs
@@ -5,8 +5,6 @@
// Execute `rustlings hint intro2` or use the `hint` watch subcommand for a
// hint.
-// I AM NOT DONE
-
fn main() {
printline!("Hello there!")
}
diff --git a/exercises/01_variables/variables1.rs b/exercises/01_variables/variables1.rs
index b3e089a..56408f3 100644
--- a/exercises/01_variables/variables1.rs
+++ b/exercises/01_variables/variables1.rs
@@ -5,8 +5,6 @@
// Execute `rustlings hint variables1` or use the `hint` watch subcommand for a
// hint.
-// I AM NOT DONE
-
fn main() {
x = 5;
println!("x has the value {}", x);
diff --git a/exercises/01_variables/variables2.rs b/exercises/01_variables/variables2.rs
index e1c23ed..0f417e0 100644
--- a/exercises/01_variables/variables2.rs
+++ b/exercises/01_variables/variables2.rs
@@ -3,8 +3,6 @@
// Execute `rustlings hint variables2` or use the `hint` watch subcommand for a
// hint.
-// I AM NOT DONE
-
fn main() {
let x;
if x == 10 {
diff --git a/exercises/01_variables/variables3.rs b/exercises/01_variables/variables3.rs
index 86bed41..421c6b1 100644
--- a/exercises/01_variables/variables3.rs
+++ b/exercises/01_variables/variables3.rs
@@ -3,8 +3,6 @@
// Execute `rustlings hint variables3` or use the `hint` watch subcommand for a
// hint.
-// I AM NOT DONE
-
fn main() {
let x: i32;
println!("Number {}", x);
diff --git a/exercises/01_variables/variables4.rs b/exercises/01_variables/variables4.rs
index 5394f39..68f8f50 100644
--- a/exercises/01_variables/variables4.rs
+++ b/exercises/01_variables/variables4.rs
@@ -3,8 +3,6 @@
// Execute `rustlings hint variables4` or use the `hint` watch subcommand for a
// hint.
-// I AM NOT DONE
-
fn main() {
let x = 3;
println!("Number {}", x);
diff --git a/exercises/01_variables/variables5.rs b/exercises/01_variables/variables5.rs
index a29b38b..7014c56 100644
--- a/exercises/01_variables/variables5.rs
+++ b/exercises/01_variables/variables5.rs
@@ -3,8 +3,6 @@
// Execute `rustlings hint variables5` or use the `hint` watch subcommand for a
// hint.
-// I AM NOT DONE
-
fn main() {
let number = "T-H-R-E-E"; // don't change this line
println!("Spell a Number : {}", number);
diff --git a/exercises/01_variables/variables6.rs b/exercises/01_variables/variables6.rs
index 853183b..9f47682 100644
--- a/exercises/01_variables/variables6.rs
+++ b/exercises/01_variables/variables6.rs
@@ -3,8 +3,6 @@
// Execute `rustlings hint variables6` or use the `hint` watch subcommand for a
// hint.
-// I AM NOT DONE
-
const NUMBER = 3;
fn main() {
println!("Number {}", NUMBER);
diff --git a/exercises/02_functions/functions1.rs b/exercises/02_functions/functions1.rs
index 40ed9a0..2365f91 100644
--- a/exercises/02_functions/functions1.rs
+++ b/exercises/02_functions/functions1.rs
@@ -3,8 +3,6 @@
// Execute `rustlings hint functions1` or use the `hint` watch subcommand for a
// hint.
-// I AM NOT DONE
-
fn main() {
call_me();
}
diff --git a/exercises/02_functions/functions2.rs b/exercises/02_functions/functions2.rs
index 5154f34..64dbd66 100644
--- a/exercises/02_functions/functions2.rs
+++ b/exercises/02_functions/functions2.rs
@@ -3,8 +3,6 @@
// Execute `rustlings hint functions2` or use the `hint` watch subcommand for a
// hint.
-// I AM NOT DONE
-
fn main() {
call_me(3);
}
diff --git a/exercises/02_functions/functions3.rs b/exercises/02_functions/functions3.rs
index 74f44d6..5037121 100644
--- a/exercises/02_functions/functions3.rs
+++ b/exercises/02_functions/functions3.rs
@@ -3,8 +3,6 @@
// Execute `rustlings hint functions3` or use the `hint` watch subcommand for a
// hint.
-// I AM NOT DONE
-
fn main() {
call_me();
}
diff --git a/exercises/02_functions/functions4.rs b/exercises/02_functions/functions4.rs
index 77c4b2a..6b449ed 100644
--- a/exercises/02_functions/functions4.rs
+++ b/exercises/02_functions/functions4.rs
@@ -8,8 +8,6 @@
// Execute `rustlings hint functions4` or use the `hint` watch subcommand for a
// hint.
-// I AM NOT DONE
-
fn main() {
let original_price = 51;
println!("Your sale price is {}", sale_price(original_price));
diff --git a/exercises/02_functions/functions5.rs b/exercises/02_functions/functions5.rs
index f1b63f4..0c96322 100644
--- a/exercises/02_functions/functions5.rs
+++ b/exercises/02_functions/functions5.rs
@@ -3,8 +3,6 @@
// Execute `rustlings hint functions5` or use the `hint` watch subcommand for a
// hint.
-// I AM NOT DONE
-
fn main() {
let answer = square(3);
println!("The square of 3 is {}", answer);
diff --git a/exercises/03_if/if1.rs b/exercises/03_if/if1.rs
index d2afccf..a1df66b 100644
--- a/exercises/03_if/if1.rs
+++ b/exercises/03_if/if1.rs
@@ -2,8 +2,6 @@
//
// Execute `rustlings hint if1` or use the `hint` watch subcommand for a hint.
-// I AM NOT DONE
-
pub fn bigger(a: i32, b: i32) -> i32 {
// Complete this function to return the bigger number!
// If both numbers are equal, any of them can be returned.
diff --git a/exercises/03_if/if2.rs b/exercises/03_if/if2.rs
index f512f13..7b9c05f 100644
--- a/exercises/03_if/if2.rs
+++ b/exercises/03_if/if2.rs
@@ -5,8 +5,6 @@
//
// Execute `rustlings hint if2` or use the `hint` watch subcommand for a hint.
-// I AM NOT DONE
-
pub fn foo_if_fizz(fizzish: &str) -> &str {
if fizzish == "fizz" {
"foo"
diff --git a/exercises/03_if/if3.rs b/exercises/03_if/if3.rs
index 1696274..caba172 100644
--- a/exercises/03_if/if3.rs
+++ b/exercises/03_if/if3.rs
@@ -2,8 +2,6 @@
//
// Execute `rustlings hint if3` or use the `hint` watch subcommand for a hint.
-// I AM NOT DONE
-
pub fn animal_habitat(animal: &str) -> &'static str {
let identifier = if animal == "crab" {
1
diff --git a/exercises/04_primitive_types/primitive_types1.rs b/exercises/04_primitive_types/primitive_types1.rs
index 3663340..f9169c8 100644
--- a/exercises/04_primitive_types/primitive_types1.rs
+++ b/exercises/04_primitive_types/primitive_types1.rs
@@ -3,8 +3,6 @@
// Fill in the rest of the line that has code missing! No hints, there's no
// tricks, just get used to typing these :)
-// I AM NOT DONE
-
fn main() {
// Booleans (`bool`)
diff --git a/exercises/04_primitive_types/primitive_types2.rs b/exercises/04_primitive_types/primitive_types2.rs
index f1616ed..1911b12 100644
--- a/exercises/04_primitive_types/primitive_types2.rs
+++ b/exercises/04_primitive_types/primitive_types2.rs
@@ -3,8 +3,6 @@
// Fill in the rest of the line that has code missing! No hints, there's no
// tricks, just get used to typing these :)
-// I AM NOT DONE
-
fn main() {
// Characters (`char`)
diff --git a/exercises/04_primitive_types/primitive_types3.rs b/exercises/04_primitive_types/primitive_types3.rs
index 8b0de44..70a8cc2 100644
--- a/exercises/04_primitive_types/primitive_types3.rs
+++ b/exercises/04_primitive_types/primitive_types3.rs
@@ -5,8 +5,6 @@
// Execute `rustlings hint primitive_types3` or use the `hint` watch subcommand
// for a hint.
-// I AM NOT DONE
-
fn main() {
let a = ???
diff --git a/exercises/04_primitive_types/primitive_types4.rs b/exercises/04_primitive_types/primitive_types4.rs
index d44d877..8ed0a82 100644
--- a/exercises/04_primitive_types/primitive_types4.rs
+++ b/exercises/04_primitive_types/primitive_types4.rs
@@ -5,8 +5,6 @@
// Execute `rustlings hint primitive_types4` or use the `hint` watch subcommand
// for a hint.
-// I AM NOT DONE
-
#[test]
fn slice_out_of_array() {
let a = [1, 2, 3, 4, 5];
diff --git a/exercises/04_primitive_types/primitive_types5.rs b/exercises/04_primitive_types/primitive_types5.rs
index f646986..5754a3d 100644
--- a/exercises/04_primitive_types/primitive_types5.rs
+++ b/exercises/04_primitive_types/primitive_types5.rs
@@ -5,8 +5,6 @@
// Execute `rustlings hint primitive_types5` or use the `hint` watch subcommand
// for a hint.
-// I AM NOT DONE
-
fn main() {
let cat = ("Furry McFurson", 3.5);
let /* your pattern here */ = cat;
diff --git a/exercises/04_primitive_types/primitive_types6.rs b/exercises/04_primitive_types/primitive_types6.rs
index 07cc46c..5f82f10 100644
--- a/exercises/04_primitive_types/primitive_types6.rs
+++ b/exercises/04_primitive_types/primitive_types6.rs
@@ -6,8 +6,6 @@
// Execute `rustlings hint primitive_types6` or use the `hint` watch subcommand
// for a hint.
-// I AM NOT DONE
-
#[test]
fn indexing_tuple() {
let numbers = (1, 2, 3);
diff --git a/exercises/05_vecs/vecs1.rs b/exercises/05_vecs/vecs1.rs
index 65b7a7f..c64acbb 100644
--- a/exercises/05_vecs/vecs1.rs
+++ b/exercises/05_vecs/vecs1.rs
@@ -7,8 +7,6 @@
//
// Execute `rustlings hint vecs1` or use the `hint` watch subcommand for a hint.
-// I AM NOT DONE
-
fn array_and_vec() -> ([i32; 4], Vec<i32>) {
let a = [10, 20, 30, 40]; // a plain array
let v = // TODO: declare your vector here with the macro for vectors
diff --git a/exercises/05_vecs/vecs2.rs b/exercises/05_vecs/vecs2.rs
index e92c970..d64d3d1 100644
--- a/exercises/05_vecs/vecs2.rs
+++ b/exercises/05_vecs/vecs2.rs
@@ -7,8 +7,6 @@
//
// Execute `rustlings hint vecs2` or use the `hint` watch subcommand for a hint.
-// I AM NOT DONE
-
fn vec_loop(mut v: Vec<i32>) -> Vec<i32> {
for element in v.iter_mut() {
// TODO: Fill this up so that each element in the Vec `v` is
diff --git a/exercises/06_move_semantics/move_semantics1.rs b/exercises/06_move_semantics/move_semantics1.rs
index e063937..c612ba9 100644
--- a/exercises/06_move_semantics/move_semantics1.rs
+++ b/exercises/06_move_semantics/move_semantics1.rs
@@ -3,8 +3,6 @@
// Execute `rustlings hint move_semantics1` or use the `hint` watch subcommand
// for a hint.
-// I AM NOT DONE
-
#[test]
fn main() {
let vec0 = vec![22, 44, 66];
diff --git a/exercises/06_move_semantics/move_semantics2.rs b/exercises/06_move_semantics/move_semantics2.rs
index dc58be5..3457d11 100644
--- a/exercises/06_move_semantics/move_semantics2.rs
+++ b/exercises/06_move_semantics/move_semantics2.rs
@@ -5,8 +5,6 @@
// Execute `rustlings hint move_semantics2` or use the `hint` watch subcommand
// for a hint.
-// I AM NOT DONE
-
#[test]
fn main() {
let vec0 = vec![22, 44, 66];
diff --git a/exercises/06_move_semantics/move_semantics3.rs b/exercises/06_move_semantics/move_semantics3.rs
index 7152c71..9415eb1 100644
--- a/exercises/06_move_semantics/move_semantics3.rs
+++ b/exercises/06_move_semantics/move_semantics3.rs
@@ -6,8 +6,6 @@
// Execute `rustlings hint move_semantics3` or use the `hint` watch subcommand
// for a hint.
-// I AM NOT DONE
-
#[test]
fn main() {
let vec0 = vec![22, 44, 66];
diff --git a/exercises/06_move_semantics/move_semantics4.rs b/exercises/06_move_semantics/move_semantics4.rs
index bfc917f..1509f5d 100644
--- a/exercises/06_move_semantics/move_semantics4.rs
+++ b/exercises/06_move_semantics/move_semantics4.rs
@@ -7,8 +7,6 @@
// Execute `rustlings hint move_semantics4` or use the `hint` watch subcommand
// for a hint.
-// I AM NOT DONE
-
#[test]
fn main() {
let vec0 = vec![22, 44, 66];
diff --git a/exercises/06_move_semantics/move_semantics5.rs b/exercises/06_move_semantics/move_semantics5.rs
index 267bdcc..c84d2fe 100644
--- a/exercises/06_move_semantics/move_semantics5.rs
+++ b/exercises/06_move_semantics/move_semantics5.rs
@@ -6,8 +6,6 @@
// Execute `rustlings hint move_semantics5` or use the `hint` watch subcommand
// for a hint.
-// I AM NOT DONE
-
#[test]
fn main() {
let mut x = 100;
diff --git a/exercises/06_move_semantics/move_semantics6.rs b/exercises/06_move_semantics/move_semantics6.rs
index cace4ca..6059e61 100644
--- a/exercises/06_move_semantics/move_semantics6.rs
+++ b/exercises/06_move_semantics/move_semantics6.rs
@@ -5,8 +5,6 @@
// Execute `rustlings hint move_semantics6` or use the `hint` watch subcommand
// for a hint.
-// I AM NOT DONE
-
fn main() {
let data = "Rust is great!".to_string();
diff --git a/exercises/07_structs/structs1.rs b/exercises/07_structs/structs1.rs
index 5fa5821..2978121 100644
--- a/exercises/07_structs/structs1.rs
+++ b/exercises/07_structs/structs1.rs
@@ -5,8 +5,6 @@
// Execute `rustlings hint structs1` or use the `hint` watch subcommand for a
// hint.
-// I AM NOT DONE
-
struct ColorClassicStruct {
// TODO: Something goes here
}
diff --git a/exercises/07_structs/structs2.rs b/exercises/07_structs/structs2.rs
index 328567f..a7a2dec 100644
--- a/exercises/07_structs/structs2.rs
+++ b/exercises/07_structs/structs2.rs
@@ -5,8 +5,6 @@
// Execute `rustlings hint structs2` or use the `hint` watch subcommand for a
// hint.
-// I AM NOT DONE
-
#[derive(Debug)]
struct Order {
name: String,
diff --git a/exercises/07_structs/structs3.rs b/exercises/07_structs/structs3.rs
index 7cda5af..9835b81 100644
--- a/exercises/07_structs/structs3.rs
+++ b/exercises/07_structs/structs3.rs
@@ -7,8 +7,6 @@
// Execute `rustlings hint structs3` or use the `hint` watch subcommand for a
// hint.
-// I AM NOT DONE
-
#[derive(Debug)]
struct Package {
sender_country: String,
diff --git a/exercises/08_enums/enums1.rs b/exercises/08_enums/enums1.rs
index 25525b2..330269c 100644
--- a/exercises/08_enums/enums1.rs
+++ b/exercises/08_enums/enums1.rs
@@ -2,8 +2,6 @@
//
// No hints this time! ;)
-// I AM NOT DONE
-
#[derive(Debug)]
enum Message {
// TODO: define a few types of messages as used below
diff --git a/exercises/08_enums/enums2.rs b/exercises/08_enums/enums2.rs
index df93fe0..f0e4e6d 100644
--- a/exercises/08_enums/enums2.rs
+++ b/exercises/08_enums/enums2.rs
@@ -3,8 +3,6 @@
// Execute `rustlings hint enums2` or use the `hint` watch subcommand for a
// hint.
-// I AM NOT DONE
-
#[derive(Debug)]
enum Message {
// TODO: define the different variants used below
diff --git a/exercises/08_enums/enums3.rs b/exercises/08_enums/enums3.rs
index 92d18c4..580a553 100644
--- a/exercises/08_enums/enums3.rs
+++ b/exercises/08_enums/enums3.rs
@@ -5,8 +5,6 @@
// Execute `rustlings hint enums3` or use the `hint` watch subcommand for a
// hint.
-// I AM NOT DONE
-
enum Message {
// TODO: implement the message variant types based on their usage below
}
diff --git a/exercises/09_strings/strings1.rs b/exercises/09_strings/strings1.rs
index f50e1fa..a1255a3 100644
--- a/exercises/09_strings/strings1.rs
+++ b/exercises/09_strings/strings1.rs
@@ -5,8 +5,6 @@
// Execute `rustlings hint strings1` or use the `hint` watch subcommand for a
// hint.
-// I AM NOT DONE
-
fn main() {
let answer = current_favorite_color();
println!("My current favorite color is {}", answer);
diff --git a/exercises/09_strings/strings2.rs b/exercises/09_strings/strings2.rs
index 4d95d16..ba76fe6 100644
--- a/exercises/09_strings/strings2.rs
+++ b/exercises/09_strings/strings2.rs
@@ -5,8 +5,6 @@
// Execute `rustlings hint strings2` or use the `hint` watch subcommand for a
// hint.
-// I AM NOT DONE
-
fn main() {
let word = String::from("green"); // Try not changing this line :)
if is_a_color_word(word) {
diff --git a/exercises/09_strings/strings3.rs b/exercises/09_strings/strings3.rs
index 384e7ce..dedc081 100644
--- a/exercises/09_strings/strings3.rs
+++ b/exercises/09_strings/strings3.rs
@@ -3,8 +3,6 @@
// Execute `rustlings hint strings3` or use the `hint` watch subcommand for a
// hint.
-// I AM NOT DONE
-
fn trim_me(input: &str) -> String {
// TODO: Remove whitespace from both ends of a string!
???
diff --git a/exercises/09_strings/strings4.rs b/exercises/09_strings/strings4.rs
index e8c54ac..a034aa4 100644
--- a/exercises/09_strings/strings4.rs
+++ b/exercises/09_strings/strings4.rs
@@ -7,8 +7,6 @@
//
// No hints this time!
-// I AM NOT DONE
-
fn string_slice(arg: &str) {
println!("{}", arg);
}
diff --git a/exercises/10_modules/modules1.rs b/exercises/10_modules/modules1.rs
index 9eb5a48..c750946 100644
--- a/exercises/10_modules/modules1.rs
+++ b/exercises/10_modules/modules1.rs
@@ -3,8 +3,6 @@
// Execute `rustlings hint modules1` or use the `hint` watch subcommand for a
// hint.
-// I AM NOT DONE
-
mod sausage_factory {
// Don't let anybody outside of this module see this!
fn get_secret_recipe() -> String {
diff --git a/exercises/10_modules/modules2.rs b/exercises/10_modules/modules2.rs
index 0415454..4d3106c 100644
--- a/exercises/10_modules/modules2.rs
+++ b/exercises/10_modules/modules2.rs
@@ -7,8 +7,6 @@
// Execute `rustlings hint modules2` or use the `hint` watch subcommand for a
// hint.
-// I AM NOT DONE
-
mod delicious_snacks {
// TODO: Fix these use statements
use self::fruits::PEAR as ???
diff --git a/exercises/10_modules/modules3.rs b/exercises/10_modules/modules3.rs
index f2bb050..c211a76 100644
--- a/exercises/10_modules/modules3.rs
+++ b/exercises/10_modules/modules3.rs
@@ -8,8 +8,6 @@
// Execute `rustlings hint modules3` or use the `hint` watch subcommand for a
// hint.
-// I AM NOT DONE
-
// TODO: Complete this use statement
use ???
diff --git a/exercises/11_hashmaps/hashmaps1.rs b/exercises/11_hashmaps/hashmaps1.rs
index 80829ea..5a52f61 100644
--- a/exercises/11_hashmaps/hashmaps1.rs
+++ b/exercises/11_hashmaps/hashmaps1.rs
@@ -11,8 +11,6 @@
// Execute `rustlings hint hashmaps1` or use the `hint` watch subcommand for a
// hint.
-// I AM NOT DONE
-
use std::collections::HashMap;
fn fruit_basket() -> HashMap<String, u32> {
diff --git a/exercises/11_hashmaps/hashmaps2.rs b/exercises/11_hashmaps/hashmaps2.rs
index a592569..2730643 100644
--- a/exercises/11_hashmaps/hashmaps2.rs
+++ b/exercises/11_hashmaps/hashmaps2.rs
@@ -14,8 +14,6 @@
// Execute `rustlings hint hashmaps2` or use the `hint` watch subcommand for a
// hint.
-// I AM NOT DONE
-
use std::collections::HashMap;
#[derive(Hash, PartialEq, Eq)]
diff --git a/exercises/11_hashmaps/hashmaps3.rs b/exercises/11_hashmaps/hashmaps3.rs
index 8d9236d..775a401 100644
--- a/exercises/11_hashmaps/hashmaps3.rs
+++ b/exercises/11_hashmaps/hashmaps3.rs
@@ -15,8 +15,6 @@
// Execute `rustlings hint hashmaps3` or use the `hint` watch subcommand for a
// hint.
-// I AM NOT DONE
-
use std::collections::HashMap;
// A structure to store the goal details of a team.
diff --git a/exercises/12_options/options1.rs b/exercises/12_options/options1.rs
index 3cbfecd..ba4b1cd 100644
--- a/exercises/12_options/options1.rs
+++ b/exercises/12_options/options1.rs
@@ -3,8 +3,6 @@
// Execute `rustlings hint options1` or use the `hint` watch subcommand for a
// hint.
-// I AM NOT DONE
-
// This function returns how much icecream there is left in the fridge.
// If it's before 10PM, there's 5 scoops left. At 10PM, someone eats it
// all, so there'll be no more left :(
diff --git a/exercises/12_options/options2.rs b/exercises/12_options/options2.rs
index 4d998e7..73f707e 100644
--- a/exercises/12_options/options2.rs
+++ b/exercises/12_options/options2.rs
@@ -3,8 +3,6 @@
// Execute `rustlings hint options2` or use the `hint` watch subcommand for a
// hint.
-// I AM NOT DONE
-
#[cfg(test)]
mod tests {
#[test]
diff --git a/exercises/12_options/options3.rs b/exercises/12_options/options3.rs
index 23c15ea..7922ef9 100644
--- a/exercises/12_options/options3.rs
+++ b/exercises/12_options/options3.rs
@@ -3,8 +3,6 @@
// Execute `rustlings hint options3` or use the `hint` watch subcommand for a
// hint.
-// I AM NOT DONE
-
struct Point {
x: i32,
y: i32,
diff --git a/exercises/13_error_handling/errors1.rs b/exercises/13_error_handling/errors1.rs
index 0ba59a5..9767f2c 100644
--- a/exercises/13_error_handling/errors1.rs
+++ b/exercises/13_error_handling/errors1.rs
@@ -9,8 +9,6 @@
// Execute `rustlings hint errors1` or use the `hint` watch subcommand for a
// hint.
-// I AM NOT DONE
-
pub fn generate_nametag_text(name: String) -> Option<String> {
if name.is_empty() {
// Empty names aren't allowed.
diff --git a/exercises/13_error_handling/errors2.rs b/exercises/13_error_handling/errors2.rs
index 631fe67..88d1bf4 100644
--- a/exercises/13_error_handling/errors2.rs
+++ b/exercises/13_error_handling/errors2.rs
@@ -19,8 +19,6 @@
// Execute `rustlings hint errors2` or use the `hint` watch subcommand for a
// hint.
-// I AM NOT DONE
-
use std::num::ParseIntError;
pub fn total_cost(item_quantity: &str) -> Result<i32, ParseIntError> {
diff --git a/exercises/13_error_handling/errors3.rs b/exercises/13_error_handling/errors3.rs
index d42d3b1..56bb31b 100644
--- a/exercises/13_error_handling/errors3.rs
+++ b/exercises/13_error_handling/errors3.rs
@@ -7,8 +7,6 @@
// Execute `rustlings hint errors3` or use the `hint` watch subcommand for a
// hint.
-// I AM NOT DONE
-
use std::num::ParseIntError;
fn main() {
diff --git a/exercises/13_error_handling/errors4.rs b/exercises/13_error_handling/errors4.rs
index d6d6fcb..0e5c08b 100644
--- a/exercises/13_error_handling/errors4.rs
+++ b/exercises/13_error_handling/errors4.rs
@@ -3,8 +3,6 @@
// Execute `rustlings hint errors4` or use the `hint` watch subcommand for a
// hint.
-// I AM NOT DONE
-
#[derive(PartialEq, Debug)]
struct PositiveNonzeroInteger(u64);
diff --git a/exercises/13_error_handling/errors5.rs b/exercises/13_error_handling/errors5.rs
index 92461a7..0bcb4b8 100644
--- a/exercises/13_error_handling/errors5.rs
+++ b/exercises/13_error_handling/errors5.rs
@@ -22,8 +22,6 @@
// Execute `rustlings hint errors5` or use the `hint` watch subcommand for a
// hint.
-// I AM NOT DONE
-
use std::error;
use std::fmt;
use std::num::ParseIntError;
diff --git a/exercises/13_error_handling/errors6.rs b/exercises/13_error_handling/errors6.rs
index aaf0948..de73a9a 100644
--- a/exercises/13_error_handling/errors6.rs
+++ b/exercises/13_error_handling/errors6.rs
@@ -9,8 +9,6 @@
// Execute `rustlings hint errors6` or use the `hint` watch subcommand for a
// hint.
-// I AM NOT DONE
-
use std::num::ParseIntError;
// This is a custom error type that we will be using in `parse_pos_nonzero()`.
diff --git a/exercises/14_generics/generics1.rs b/exercises/14_generics/generics1.rs
index 35c1d2f..545fd95 100644
--- a/exercises/14_generics/generics1.rs
+++ b/exercises/14_generics/generics1.rs
@@ -6,8 +6,6 @@
// Execute `rustlings hint generics1` or use the `hint` watch subcommand for a
// hint.
-// I AM NOT DONE
-
fn main() {
let mut shopping_list: Vec<?> = Vec::new();
shopping_list.push("milk");
diff --git a/exercises/14_generics/generics2.rs b/exercises/14_generics/generics2.rs
index 074cd93..d50ed17 100644
--- a/exercises/14_generics/generics2.rs
+++ b/exercises/14_generics/generics2.rs
@@ -6,8 +6,6 @@
// Execute `rustlings hint generics2` or use the `hint` watch subcommand for a
// hint.
-// I AM NOT DONE
-
struct Wrapper {
value: u32,
}
diff --git a/exercises/15_traits/traits1.rs b/exercises/15_traits/traits1.rs
index 37dfcbf..c51d3b8 100644
--- a/exercises/15_traits/traits1.rs
+++ b/exercises/15_traits/traits1.rs
@@ -7,8 +7,6 @@
// Execute `rustlings hint traits1` or use the `hint` watch subcommand for a
// hint.
-// I AM NOT DONE
-
trait AppendBar {
fn append_bar(self) -> Self;
}
diff --git a/exercises/15_traits/traits2.rs b/exercises/15_traits/traits2.rs
index 3e35f8e..9a2bc07 100644
--- a/exercises/15_traits/traits2.rs
+++ b/exercises/15_traits/traits2.rs
@@ -8,8 +8,6 @@
//
// Execute `rustlings hint traits2` or use the `hint` watch subcommand for a hint.
-// I AM NOT DONE
-
trait AppendBar {
fn append_bar(self) -> Self;
}
diff --git a/exercises/15_traits/traits3.rs b/exercises/15_traits/traits3.rs
index 4e2b06b..357f1d7 100644
--- a/exercises/15_traits/traits3.rs
+++ b/exercises/15_traits/traits3.rs
@@ -8,8 +8,6 @@
// Execute `rustlings hint traits3` or use the `hint` watch subcommand for a
// hint.
-// I AM NOT DONE
-
pub trait Licensed {
fn licensing_info(&self) -> String;
}
diff --git a/exercises/15_traits/traits4.rs b/exercises/15_traits/traits4.rs
index 4bda3e5..7242c48 100644
--- a/exercises/15_traits/traits4.rs
+++ b/exercises/15_traits/traits4.rs
@@ -7,8 +7,6 @@
// Execute `rustlings hint traits4` or use the `hint` watch subcommand for a
// hint.
-// I AM NOT DONE
-
pub trait Licensed {
fn licensing_info(&self) -> String {
"some information".to_string()
diff --git a/exercises/15_traits/traits5.rs b/exercises/15_traits/traits5.rs
index df18380..f258d32 100644
--- a/exercises/15_traits/traits5.rs
+++ b/exercises/15_traits/traits5.rs
@@ -7,8 +7,6 @@
// Execute `rustlings hint traits5` or use the `hint` watch subcommand for a
// hint.
-// I AM NOT DONE
-
pub trait SomeTrait {
fn some_function(&self) -> bool {
true
diff --git a/exercises/16_lifetimes/lifetimes1.rs b/exercises/16_lifetimes/lifetimes1.rs
index 87bde49..4f544b4 100644
--- a/exercises/16_lifetimes/lifetimes1.rs
+++ b/exercises/16_lifetimes/lifetimes1.rs
@@ -8,8 +8,6 @@
// Execute `rustlings hint lifetimes1` or use the `hint` watch subcommand for a
// hint.
-// I AM NOT DONE
-
fn longest(x: &str, y: &str) -> &str {
if x.len() > y.len() {
x
diff --git a/exercises/16_lifetimes/lifetimes2.rs b/exercises/16_lifetimes/lifetimes2.rs
index 4f3d8c1..33b5565 100644
--- a/exercises/16_lifetimes/lifetimes2.rs
+++ b/exercises/16_lifetimes/lifetimes2.rs
@@ -6,8 +6,6 @@
// Execute `rustlings hint lifetimes2` or use the `hint` watch subcommand for a
// hint.
-// I AM NOT DONE
-
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
if x.len() > y.len() {
x
diff --git a/exercises/16_lifetimes/lifetimes3.rs b/exercises/16_lifetimes/lifetimes3.rs
index 9c59f9c..de6005e 100644
--- a/exercises/16_lifetimes/lifetimes3.rs
+++ b/exercises/16_lifetimes/lifetimes3.rs
@@ -5,8 +5,6 @@
// Execute `rustlings hint lifetimes3` or use the `hint` watch subcommand for a
// hint.
-// I AM NOT DONE
-
struct Book {
author: &str,
title: &str,
diff --git a/exercises/17_tests/tests1.rs b/exercises/17_tests/tests1.rs
index 810277a..bde2108 100644
--- a/exercises/17_tests/tests1.rs
+++ b/exercises/17_tests/tests1.rs
@@ -10,8 +10,6 @@
// Execute `rustlings hint tests1` or use the `hint` watch subcommand for a
// hint.
-// I AM NOT DONE
-
#[cfg(test)]
mod tests {
#[test]
diff --git a/exercises/17_tests/tests2.rs b/exercises/17_tests/tests2.rs
index f8024e9..aea5c0e 100644
--- a/exercises/17_tests/tests2.rs
+++ b/exercises/17_tests/tests2.rs
@@ -6,8 +6,6 @@
// Execute `rustlings hint tests2` or use the `hint` watch subcommand for a
// hint.
-// I AM NOT DONE
-
#[cfg(test)]
mod tests {
#[test]
diff --git a/exercises/17_tests/tests3.rs b/exercises/17_tests/tests3.rs
index 4013e38..d815e05 100644
--- a/exercises/17_tests/tests3.rs
+++ b/exercises/17_tests/tests3.rs
@@ -7,8 +7,6 @@
// Execute `rustlings hint tests3` or use the `hint` watch subcommand for a
// hint.
-// I AM NOT DONE
-
pub fn is_even(num: i32) -> bool {
num % 2 == 0
}
diff --git a/exercises/17_tests/tests4.rs b/exercises/17_tests/tests4.rs
index 935d0db..0972a5b 100644
--- a/exercises/17_tests/tests4.rs
+++ b/exercises/17_tests/tests4.rs
@@ -5,8 +5,6 @@
// Execute `rustlings hint tests4` or use the `hint` watch subcommand for a
// hint.
-// I AM NOT DONE
-
struct Rectangle {
width: i32,
height: i32
diff --git a/exercises/18_iterators/iterators1.rs b/exercises/18_iterators/iterators1.rs
index 31076bb..7ec7da2 100644
--- a/exercises/18_iterators/iterators1.rs
+++ b/exercises/18_iterators/iterators1.rs
@@ -9,8 +9,6 @@
// Execute `rustlings hint iterators1` or use the `hint` watch subcommand for a
// hint.
-// I AM NOT DONE
-
#[test]
fn main() {
let my_fav_fruits = vec!["banana", "custard apple", "avocado", "peach", "raspberry"];
diff --git a/exercises/18_iterators/iterators2.rs b/exercises/18_iterators/iterators2.rs
index dda82a0..4ca7742 100644
--- a/exercises/18_iterators/iterators2.rs
+++ b/exercises/18_iterators/iterators2.rs
@@ -6,8 +6,6 @@
// Execute `rustlings hint iterators2` or use the `hint` watch subcommand for a
// hint.
-// I AM NOT DONE
-
// Step 1.
// Complete the `capitalize_first` function.
// "hello" -> "Hello"
diff --git a/exercises/18_iterators/iterators3.rs b/exercises/18_iterators/iterators3.rs
index 29fa23a..f7da049 100644
--- a/exercises/18_iterators/iterators3.rs
+++ b/exercises/18_iterators/iterators3.rs
@@ -9,8 +9,6 @@
// Execute `rustlings hint iterators3` or use the `hint` watch subcommand for a
// hint.
-// I AM NOT DONE
-
#[derive(Debug, PartialEq, Eq)]
pub enum DivisionError {
NotDivisible(NotDivisibleError),
diff --git a/exercises/18_iterators/iterators4.rs b/exercises/18_iterators/iterators4.rs
index 3c0724e..af3958c 100644
--- a/exercises/18_iterators/iterators4.rs
+++ b/exercises/18_iterators/iterators4.rs
@@ -3,8 +3,6 @@
// Execute `rustlings hint iterators4` or use the `hint` watch subcommand for a
// hint.
-// I AM NOT DONE
-
pub fn factorial(num: u64) -> u64 {
// Complete this function to return the factorial of num
// Do not use:
diff --git a/exercises/18_iterators/iterators5.rs b/exercises/18_iterators/iterators5.rs
index a062ee4..ceec536 100644
--- a/exercises/18_iterators/iterators5.rs
+++ b/exercises/18_iterators/iterators5.rs
@@ -11,8 +11,6 @@
// Execute `rustlings hint iterators5` or use the `hint` watch subcommand for a
// hint.
-// I AM NOT DONE
-
use std::collections::HashMap;
#[derive(Clone, Copy, PartialEq, Eq)]
diff --git a/exercises/19_smart_pointers/arc1.rs b/exercises/19_smart_pointers/arc1.rs
index 3526ddc..0647eea 100644
--- a/exercises/19_smart_pointers/arc1.rs
+++ b/exercises/19_smart_pointers/arc1.rs
@@ -21,8 +21,6 @@
//
// Execute `rustlings hint arc1` or use the `hint` watch subcommand for a hint.
-// I AM NOT DONE
-
#![forbid(unused_imports)] // Do not change this, (or the next) line.
use std::sync::Arc;
use std::thread;
diff --git a/exercises/19_smart_pointers/box1.rs b/exercises/19_smart_pointers/box1.rs
index 513e7da..2abc024 100644
--- a/exercises/19_smart_pointers/box1.rs
+++ b/exercises/19_smart_pointers/box1.rs
@@ -18,8 +18,6 @@
//
// Execute `rustlings hint box1` or use the `hint` watch subcommand for a hint.
-// I AM NOT DONE
-
#[derive(PartialEq, Debug)]
pub enum List {
Cons(i32, List),
diff --git a/exercises/19_smart_pointers/cow1.rs b/exercises/19_smart_pointers/cow1.rs
index fcd3e0b..b24591b 100644
--- a/exercises/19_smart_pointers/cow1.rs
+++ b/exercises/19_smart_pointers/cow1.rs
@@ -12,8 +12,6 @@
//
// Execute `rustlings hint cow1` or use the `hint` watch subcommand for a hint.
-// I AM NOT DONE
-
use std::borrow::Cow;
fn abs_all<'a, 'b>(input: &'a mut Cow<'b, [i32]>) -> &'a mut Cow<'b, [i32]> {
diff --git a/exercises/19_smart_pointers/rc1.rs b/exercises/19_smart_pointers/rc1.rs
index 1b90346..e96e625 100644
--- a/exercises/19_smart_pointers/rc1.rs
+++ b/exercises/19_smart_pointers/rc1.rs
@@ -10,8 +10,6 @@
//
// Execute `rustlings hint rc1` or use the `hint` watch subcommand for a hint.
-// I AM NOT DONE
-
use std::rc::Rc;
#[derive(Debug)]
diff --git a/exercises/20_threads/threads1.rs b/exercises/20_threads/threads1.rs
index 80b6def..be1301d 100644
--- a/exercises/20_threads/threads1.rs
+++ b/exercises/20_threads/threads1.rs
@@ -8,8 +8,6 @@
// Execute `rustlings hint threads1` or use the `hint` watch subcommand for a
// hint.
-// I AM NOT DONE
-
use std::thread;
use std::time::{Duration, Instant};
diff --git a/exercises/20_threads/threads2.rs b/exercises/20_threads/threads2.rs
index 60d6824..13cb840 100644
--- a/exercises/20_threads/threads2.rs
+++ b/exercises/20_threads/threads2.rs
@@ -7,8 +7,6 @@
// Execute `rustlings hint threads2` or use the `hint` watch subcommand for a
// hint.
-// I AM NOT DONE
-
use std::sync::Arc;
use std::thread;
use std::time::Duration;
diff --git a/exercises/20_threads/threads3.rs b/exercises/20_threads/threads3.rs
index acb97b4..35b914a 100644
--- a/exercises/20_threads/threads3.rs
+++ b/exercises/20_threads/threads3.rs
@@ -3,8 +3,6 @@
// Execute `rustlings hint threads3` or use the `hint` watch subcommand for a
// hint.
-// I AM NOT DONE
-
use std::sync::mpsc;
use std::sync::Arc;
use std::thread;
diff --git a/exercises/21_macros/macros1.rs b/exercises/21_macros/macros1.rs
index 678de6e..65986db 100644
--- a/exercises/21_macros/macros1.rs
+++ b/exercises/21_macros/macros1.rs
@@ -3,8 +3,6 @@
// Execute `rustlings hint macros1` or use the `hint` watch subcommand for a
// hint.
-// I AM NOT DONE
-
macro_rules! my_macro {
() => {
println!("Check out my macro!");
diff --git a/exercises/21_macros/macros2.rs b/exercises/21_macros/macros2.rs
index 788fc16..b7c37fd 100644
--- a/exercises/21_macros/macros2.rs
+++ b/exercises/21_macros/macros2.rs
@@ -3,8 +3,6 @@
// Execute `rustlings hint macros2` or use the `hint` watch subcommand for a
// hint.
-// I AM NOT DONE
-
fn main() {
my_macro!();
}
diff --git a/exercises/21_macros/macros3.rs b/exercises/21_macros/macros3.rs
index b795c14..92a1922 100644
--- a/exercises/21_macros/macros3.rs
+++ b/exercises/21_macros/macros3.rs
@@ -5,8 +5,6 @@
// Execute `rustlings hint macros3` or use the `hint` watch subcommand for a
// hint.
-// I AM NOT DONE
-
mod macros {
macro_rules! my_macro {
() => {
diff --git a/exercises/21_macros/macros4.rs b/exercises/21_macros/macros4.rs
index 71b45a0..83a6e44 100644
--- a/exercises/21_macros/macros4.rs
+++ b/exercises/21_macros/macros4.rs
@@ -3,8 +3,6 @@
// Execute `rustlings hint macros4` or use the `hint` watch subcommand for a
// hint.
-// I AM NOT DONE
-
#[rustfmt::skip]
macro_rules! my_macro {
() => {
diff --git a/exercises/22_clippy/clippy1.rs b/exercises/22_clippy/clippy1.rs
index e0c6ce7c4..1e0f42e 100644
--- a/exercises/22_clippy/clippy1.rs
+++ b/exercises/22_clippy/clippy1.rs
@@ -9,8 +9,6 @@
// Execute `rustlings hint clippy1` or use the `hint` watch subcommand for a
// hint.
-// I AM NOT DONE
-
use std::f32;
fn main() {
diff --git a/exercises/22_clippy/clippy2.rs b/exercises/22_clippy/clippy2.rs
index 9b87a0b..37ac089 100644
--- a/exercises/22_clippy/clippy2.rs
+++ b/exercises/22_clippy/clippy2.rs
@@ -3,8 +3,6 @@
// Execute `rustlings hint clippy2` or use the `hint` watch subcommand for a
// hint.
-// I AM NOT DONE
-
fn main() {
let mut res = 42;
let option = Some(12);
diff --git a/exercises/22_clippy/clippy3.rs b/exercises/22_clippy/clippy3.rs
index 5a95f5b..6a6a36b 100644
--- a/exercises/22_clippy/clippy3.rs
+++ b/exercises/22_clippy/clippy3.rs
@@ -3,8 +3,6 @@
// Here's a couple more easy Clippy fixes, so you can see its utility.
// No hints.
-// I AM NOT DONE
-
#[allow(unused_variables, unused_assignments)]
fn main() {
let my_option: Option<()> = None;
diff --git a/exercises/23_conversions/as_ref_mut.rs b/exercises/23_conversions/as_ref_mut.rs
index 2ba9e3f..cd2c93b 100644
--- a/exercises/23_conversions/as_ref_mut.rs
+++ b/exercises/23_conversions/as_ref_mut.rs
@@ -7,8 +7,6 @@
// Execute `rustlings hint as_ref_mut` or use the `hint` watch subcommand for a
// hint.
-// I AM NOT DONE
-
// Obtain the number of bytes (not characters) in the given argument.
// TODO: Add the AsRef trait appropriately as a trait bound.
fn byte_counter<T>(arg: T) -> usize {
diff --git a/exercises/23_conversions/from_into.rs b/exercises/23_conversions/from_into.rs
index 11787c3..d2a1609 100644
--- a/exercises/23_conversions/from_into.rs
+++ b/exercises/23_conversions/from_into.rs
@@ -41,8 +41,6 @@ impl Default for Person {
// If while parsing the age, something goes wrong, then return the default of
// Person Otherwise, then return an instantiated Person object with the results
-// I AM NOT DONE
-
impl From<&str> for Person {
fn from(s: &str) -> Person {}
}
diff --git a/exercises/23_conversions/from_str.rs b/exercises/23_conversions/from_str.rs
index e209347..ed91ca5 100644
--- a/exercises/23_conversions/from_str.rs
+++ b/exercises/23_conversions/from_str.rs
@@ -31,8 +31,6 @@ enum ParsePersonError {
ParseInt(ParseIntError),
}
-// I AM NOT DONE
-
// Steps:
// 1. If the length of the provided string is 0, an error should be returned
// 2. Split the given string on the commas present in it
diff --git a/exercises/23_conversions/try_from_into.rs b/exercises/23_conversions/try_from_into.rs
index 32d6ef3..2316655 100644
--- a/exercises/23_conversions/try_from_into.rs
+++ b/exercises/23_conversions/try_from_into.rs
@@ -27,8 +27,6 @@ enum IntoColorError {
IntConversion,
}
-// I AM NOT DONE
-
// Your task is to complete this implementation and return an Ok result of inner
// type Color. You need to create an implementation for a tuple of three
// integers, an array of three integers, and a slice of integers.
diff --git a/exercises/23_conversions/using_as.rs b/exercises/23_conversions/using_as.rs
index 414cef3..9f617ec 100644
--- a/exercises/23_conversions/using_as.rs
+++ b/exercises/23_conversions/using_as.rs
@@ -10,8 +10,6 @@
// Execute `rustlings hint using_as` or use the `hint` watch subcommand for a
// hint.
-// I AM NOT DONE
-
fn average(values: &[f64]) -> f64 {
let total = values.iter().sum::<f64>();
total / values.len()
diff --git a/exercises/quiz1.rs b/exercises/quiz1.rs
index 4ee5ada..b9e71f5 100644
--- a/exercises/quiz1.rs
+++ b/exercises/quiz1.rs
@@ -13,8 +13,6 @@
//
// No hints this time ;)
-// I AM NOT DONE
-
// Put your function here!
// fn calculate_price_of_apples {
diff --git a/exercises/quiz2.rs b/exercises/quiz2.rs
index 29925ca..8ace3fe 100644
--- a/exercises/quiz2.rs
+++ b/exercises/quiz2.rs
@@ -20,8 +20,6 @@
//
// No hints this time!
-// I AM NOT DONE
-
pub enum Command {
Uppercase,
Trim,
diff --git a/exercises/quiz3.rs b/exercises/quiz3.rs
index 3b01d31..24f7082 100644
--- a/exercises/quiz3.rs
+++ b/exercises/quiz3.rs
@@ -16,8 +16,6 @@
//
// Execute `rustlings hint quiz3` or use the `hint` watch subcommand for a hint.
-// I AM NOT DONE
-
pub struct ReportCard {
pub grade: f32,
pub student_name: String,
diff --git a/info.toml b/info.toml
index 36629b3..c085e89 100644
--- a/info.toml
+++ b/info.toml
@@ -4,6 +4,7 @@
name = "intro1"
path = "exercises/00_intro/intro1.rs"
mode = "compile"
+# TODO: Fix hint
hint = """
Remove the `I AM NOT DONE` comment in the `exercises/intro00/intro1.rs` file
to move on to the next exercise."""
@@ -129,11 +130,7 @@ path = "exercises/02_functions/functions3.rs"
mode = "compile"
hint = """
This time, the function *declaration* is okay, but there's something wrong
-with the place where we're calling the function.
-
-As a reminder, you can freely play around with different solutions in Rustlings!
-Watch mode will only jump to the next exercise if you remove the `I AM NOT
-DONE` comment."""
+with the place where we're calling the function."""
[[exercises]]
name = "functions4"
diff --git a/src/app_state.rs b/src/app_state.rs
new file mode 100644
index 0000000..4a0912e
--- /dev/null
+++ b/src/app_state.rs
@@ -0,0 +1,185 @@
+use anyhow::{bail, Context, Result};
+use serde::{Deserialize, Serialize};
+use std::fs;
+
+use crate::exercise::Exercise;
+
+const BAD_INDEX_ERR: &str = "The current exercise index is higher than the number of exercises";
+
+#[derive(Serialize, Deserialize)]
+#[serde(deny_unknown_fields)]
+struct StateFile {
+ current_exercise_ind: usize,
+ progress: Vec<bool>,
+}
+
+impl StateFile {
+ fn read(exercises: &[Exercise]) -> Option<Self> {
+ let file_content = fs::read(".rustlings-state.json").ok()?;
+
+ let slf: Self = serde_json::de::from_slice(&file_content).ok()?;
+
+ if slf.progress.len() != exercises.len() || slf.current_exercise_ind >= exercises.len() {
+ return None;
+ }
+
+ Some(slf)
+ }
+
+ fn read_or_default(exercises: &[Exercise]) -> Self {
+ Self::read(exercises).unwrap_or_else(|| Self {
+ current_exercise_ind: 0,
+ progress: vec![false; exercises.len()],
+ })
+ }
+
+ fn write(&self) -> Result<()> {
+ let mut buf = Vec::with_capacity(1024);
+ serde_json::ser::to_writer(&mut buf, self).context("Failed to serialize the state")?;
+ fs::write(".rustlings-state.json", buf)
+ .context("Failed to write the state file `.rustlings-state.json`")?;
+
+ Ok(())
+ }
+}
+
+pub struct AppState {
+ state_file: StateFile,
+ exercises: &'static [Exercise],
+ n_done: u16,
+ current_exercise: &'static Exercise,
+}
+
+#[must_use]
+pub enum ExercisesProgress {
+ AllDone,
+ Pending,
+}
+
+impl AppState {
+ pub fn new(exercises: Vec<Exercise>) -> Self {
+ // Leaking for sending the exercises to the debounce event handler.
+ // Leaking is not a problem since the exercises' slice is used until the end of the program.
+ let exercises = exercises.leak();
+
+ let state_file = StateFile::read_or_default(exercises);
+ let n_done = state_file
+ .progress
+ .iter()
+ .fold(0, |acc, done| acc + u16::from(*done));
+ let current_exercise = &exercises[state_file.current_exercise_ind];
+
+ Self {
+ state_file,
+ exercises,
+ n_done,
+ current_exercise,
+ }
+ }
+
+ #[inline]
+ pub fn current_exercise_ind(&self) -> usize {
+ self.state_file.current_exercise_ind
+ }
+
+ #[inline]
+ pub fn progress(&self) -> &[bool] {
+ &self.state_file.progress
+ }
+
+ #[inline]
+ pub fn exercises(&self) -> &'static [Exercise] {
+ self.exercises
+ }
+
+ #[inline]
+ pub fn n_done(&self) -> u16 {
+ self.n_done
+ }
+
+ #[inline]
+ pub fn current_exercise(&self) -> &'static Exercise {
+ self.current_exercise
+ }
+
+ pub fn set_current_exercise_ind(&mut self, ind: usize) -> Result<()> {
+ if ind >= self.exercises.len() {
+ bail!(BAD_INDEX_ERR);
+ }
+
+ self.state_file.current_exercise_ind = ind;
+ self.current_exercise = &self.exercises[ind];
+
+ self.state_file.write()
+ }
+
+ pub fn set_current_exercise_by_name(&mut self, name: &str) -> Result<()> {
+ let (ind, exercise) = self
+ .exercises
+ .iter()
+ .enumerate()
+ .find(|(_, exercise)| exercise.name == name)
+ .with_context(|| format!("No exercise found for '{name}'!"))?;
+
+ self.state_file.current_exercise_ind = ind;
+ self.current_exercise = exercise;
+
+ self.state_file.write()
+ }
+
+ pub fn set_pending(&mut self, ind: usize) -> Result<()> {
+ let done = self
+ .state_file
+ .progress
+ .get_mut(ind)
+ .context(BAD_INDEX_ERR)?;
+
+ if *done {
+ *done = false;
+ self.n_done -= 1;
+ self.state_file.write()?;
+ }
+
+ Ok(())
+ }
+
+ fn next_exercise_ind(&self) -> Option<usize> {
+ let current_ind = self.state_file.current_exercise_ind;
+
+ if current_ind == self.state_file.progress.len() - 1 {
+ // The last exercise is done.
+ // Search for exercises not done from the start.
+ return self.state_file.progress[..current_ind]
+ .iter()
+ .position(|done| !done);
+ }
+
+ // The done exercise isn't the last one.
+ // Search for a pending exercise after the current one and then from the start.
+ match self.state_file.progress[current_ind + 1..]
+ .iter()
+ .position(|done| !done)
+ {
+ Some(ind) => Some(current_ind + 1 + ind),
+ None => self.state_file.progress[..current_ind]
+ .iter()
+ .position(|done| !done),
+ }
+ }
+
+ pub fn done_current_exercise(&mut self) -> Result<ExercisesProgress> {
+ let done = &mut self.state_file.progress[self.state_file.current_exercise_ind];
+ if !*done {
+ *done = true;
+ self.n_done += 1;
+ }
+
+ let Some(ind) = self.next_exercise_ind() else {
+ return Ok(ExercisesProgress::AllDone);
+ };
+
+ self.set_current_exercise_ind(ind)?;
+
+ Ok(ExercisesProgress::Pending)
+ }
+}
diff --git a/src/exercise.rs b/src/exercise.rs
index ca47009..de435d1 100644
--- a/src/exercise.rs
+++ b/src/exercise.rs
@@ -1,38 +1,14 @@
use anyhow::{Context, Result};
use serde::Deserialize;
use std::{
- array,
fmt::{self, Debug, Display, Formatter},
- fs::{self, File},
- io::{self, BufRead, BufReader},
- mem,
+ fs::{self},
path::PathBuf,
process::{Command, Output},
};
-use winnow::{
- ascii::{space0, Caseless},
- combinator::opt,
- Parser,
-};
use crate::embedded::{WriteStrategy, EMBEDDED_FILES};
-// The number of context lines above and below a highlighted line.
-const CONTEXT: usize = 2;
-
-// Check if the line contains the "I AM NOT DONE" comment.
-fn contains_not_done_comment(input: &str) -> bool {
- (
- space0::<_, ()>,
- "//",
- opt('/'),
- space0,
- Caseless("I AM NOT DONE"),
- )
- .parse_next(&mut &*input)
- .is_ok()
-}
-
// The mode of the exercise.
#[derive(Deserialize, Copy, Clone)]
#[serde(rename_all = "lowercase")]
@@ -78,13 +54,6 @@ pub struct Exercise {
pub hint: String,
}
-// The state of an Exercise.
-#[derive(PartialEq, Eq, Debug)]
-pub enum State {
- Done,
- Pending(Vec<ContextLine>),
-}
-
// The context information of a pending exercise.
#[derive(PartialEq, Eq, Debug)]
pub struct ContextLine {
@@ -129,105 +98,6 @@ impl Exercise {
}
}
- pub fn state(&self) -> Result<State> {
- let source_file = File::open(&self.path)
- .with_context(|| format!("Failed to open the exercise file {}", self.path.display()))?;
- let mut source_reader = BufReader::new(source_file);
-
- // Read the next line into `buf` without the newline at the end.
- let mut read_line = |buf: &mut String| -> io::Result<_> {
- let n = source_reader.read_line(buf)?;
- if buf.ends_with('\n') {
- buf.pop();
- if buf.ends_with('\r') {
- buf.pop();
- }
- }
- Ok(n)
- };
-
- let mut current_line_number: usize = 1;
- // Keep the last `CONTEXT` lines while iterating over the file lines.
- let mut prev_lines: [_; CONTEXT] = array::from_fn(|_| String::with_capacity(256));
- let mut line = String::with_capacity(256);
-
- loop {
- let n = read_line(&mut line).with_context(|| {
- format!("Failed to read the exercise file {}", self.path.display())
- })?;
-
- // Reached the end of the file and didn't find the comment.
- if n == 0 {
- return Ok(State::Done);
- }
-
- if contains_not_done_comment(&line) {
- let mut context = Vec::with_capacity(2 * CONTEXT + 1);
- // Previous lines.
- for (ind, prev_line) in prev_lines
- .into_iter()
- .take(current_line_number - 1)
- .enumerate()
- .rev()
- {
- context.push(ContextLine {
- line: prev_line,
- number: current_line_number - 1 - ind,
- important: false,
- });
- }
-
- // Current line.
- context.push(ContextLine {
- line,
- number: current_line_number,
- important: true,
- });
-
- // Next lines.
- for ind in 0..CONTEXT {
- let mut next_line = String::with_capacity(256);
- let Ok(n) = read_line(&mut next_line) else {
- // If an error occurs, just ignore the next lines.
- break;
- };
-
- // Reached the end of the file.
- if n == 0 {
- break;
- }
-
- context.push(ContextLine {
- line: next_line,
- number: current_line_number + 1 + ind,
- important: false,
- });
- }
-
- return Ok(State::Pending(context));
- }
-
- current_line_number += 1;
- // Add the current line as a previous line and shift the older lines by one.
- for prev_line in &mut prev_lines {
- mem::swap(&mut line, prev_line);
- }
- // The current line now contains the oldest previous line.
- // Recycle it for reading the next line.
- line.clear();
- }
- }
-
- // Check that the exercise looks to be solved using self.state()
- // This is not the best way to check since
- // the user can just remove the "I AM NOT DONE" string from the file
- // without actually having solved anything.
- // The only other way to truly check this would to compile and run
- // the exercise; which would be both costly and counterintuitive
- pub fn looks_done(&self) -> Result<bool> {
- self.state().map(|state| state == State::Done)
- }
-
pub fn reset(&self) -> Result<()> {
EMBEDDED_FILES
.write_exercise_to_disk(&self.path, WriteStrategy::Overwrite)
@@ -240,77 +110,3 @@ impl Display for Exercise {
self.path.fmt(f)
}
}
-
-#[cfg(test)]
-mod test {
- use super::*;
-
- #[test]
- fn test_pending_state() {
- let exercise = Exercise {
- name: "pending_exercise".into(),
- path: PathBuf::from("tests/fixture/state/exercises/pending_exercise.rs"),
- mode: Mode::Compile,
- hint: String::new(),
- };
-
- let state = exercise.state();
- let expected = vec![
- ContextLine {
- line: "// fake_exercise".to_string(),
- number: 1,
- important: false,
- },
- ContextLine {
- line: "".to_string(),
- number: 2,
- important: false,
- },
- ContextLine {
- line: "// I AM NOT DONE".to_string(),
- number: 3,
- important: true,
- },
- ContextLine {
- line: "".to_string(),
- number: 4,
- important: false,
- },
- ContextLine {
- line: "fn main() {".to_string(),
- number: 5,
- important: false,
- },
- ];
-
- assert_eq!(state.unwrap(), State::Pending(expected));
- }
-
- #[test]
- fn test_finished_exercise() {
- let exercise = Exercise {
- name: "finished_exercise".into(),
- path: PathBuf::from("tests/fixture/state/exercises/finished_exercise.rs"),
- mode: Mode::Compile,
- hint: String::new(),
- };
-
- assert_eq!(exercise.state().unwrap(), State::Done);
- }
-
- #[test]
- fn test_not_done() {
- assert!(contains_not_done_comment("// I AM NOT DONE"));
- assert!(contains_not_done_comment("/// I AM NOT DONE"));
- assert!(contains_not_done_comment("// I AM NOT DONE"));
- assert!(contains_not_done_comment("/// I AM NOT DONE"));
- assert!(contains_not_done_comment("// I AM NOT DONE "));
- assert!(contains_not_done_comment("// I AM NOT DONE!"));
- assert!(contains_not_done_comment("// I am not done"));
- assert!(contains_not_done_comment("// i am NOT done"));
-
- assert!(!contains_not_done_comment("I AM NOT DONE"));
- assert!(!contains_not_done_comment("// NOT DONE"));
- assert!(!contains_not_done_comment("DONE"));
- }
-}
diff --git a/src/list.rs b/src/list.rs
index 560b85a..80b78e8 100644
--- a/src/list.rs
+++ b/src/list.rs
@@ -9,11 +9,11 @@ use std::{fmt::Write, io};
mod state;
-use crate::{exercise::Exercise, state_file::StateFile};
+use crate::app_state::AppState;
use self::state::{Filter, UiState};
-pub fn list(state_file: &mut StateFile, exercises: &'static [Exercise]) -> Result<()> {
+pub fn list(app_state: &mut AppState) -> Result<()> {
let mut stdout = io::stdout().lock();
stdout.execute(EnterAlternateScreen)?;
enable_raw_mode()?;
@@ -21,7 +21,7 @@ pub fn list(state_file: &mut StateFile, exercises: &'static [Exercise]) -> Resul
let mut terminal = Terminal::new(CrosstermBackend::new(&mut stdout))?;
terminal.clear()?;
- let mut ui_state = UiState::new(state_file, exercises);
+ let mut ui_state = UiState::new(app_state);
'outer: loop {
terminal.draw(|frame| ui_state.draw(frame).unwrap())?;
@@ -56,7 +56,7 @@ pub fn list(state_file: &mut StateFile, exercises: &'static [Exercise]) -> Resul
"Enabled filter DONE │ Press d again to disable the filter"
};
- ui_state = ui_state.with_updated_rows(state_file);
+ ui_state = ui_state.with_updated_rows();
ui_state.message.push_str(message);
}
KeyCode::Char('p') => {
@@ -68,23 +68,20 @@ pub fn list(state_file: &mut StateFile, exercises: &'static [Exercise]) -> Resul
"Enabled filter PENDING │ Press p again to disable the filter"
};
- ui_state = ui_state.with_updated_rows(state_file);
+ ui_state = ui_state.with_updated_rows();
ui_state.message.push_str(message);
}
KeyCode::Char('r') => {
- let selected = ui_state.selected();
- let exercise = &exercises[selected];
- exercise.reset()?;
- state_file.reset(selected)?;
+ let exercise = ui_state.reset_selected()?;
- ui_state = ui_state.with_updated_rows(state_file);
+ ui_state = ui_state.with_updated_rows();
ui_state
.message
.write_fmt(format_args!("The exercise {exercise} has been reset!"))?;
}
KeyCode::Char('c') => {
- state_file.set_next_exercise_ind(ui_state.selected())?;
- ui_state = ui_state.with_updated_rows(state_file);
+ ui_state.selected_to_current_exercise()?;
+ ui_state = ui_state.with_updated_rows();
}
_ => (),
}
diff --git a/src/list/state.rs b/src/list/state.rs
index 209374b..7714268 100644
--- a/src/list/state.rs
+++ b/src/list/state.rs
@@ -7,7 +7,7 @@ use ratatui::{
Frame,
};
-use crate::{exercise::Exercise, progress_bar::progress_bar_ratatui, state_file::StateFile};
+use crate::{app_state::AppState, exercise::Exercise, progress_bar::progress_bar_ratatui};
#[derive(Copy, Clone, PartialEq, Eq)]
pub enum Filter {
@@ -16,30 +16,29 @@ pub enum Filter {
None,
}
-pub struct UiState {
+pub struct UiState<'a> {
pub table: Table<'static>,
pub message: String,
pub filter: Filter,
- exercises: &'static [Exercise],
- progress: u16,
- selected: usize,
+ app_state: &'a mut AppState,
table_state: TableState,
+ selected: usize,
last_ind: usize,
}
-impl UiState {
- pub fn with_updated_rows(mut self, state_file: &StateFile) -> Self {
+impl<'a> UiState<'a> {
+ pub fn with_updated_rows(mut self) -> Self {
+ let current_exercise_ind = self.app_state.current_exercise_ind();
+
let mut rows_counter: usize = 0;
- let mut progress: u16 = 0;
let rows = self
- .exercises
+ .app_state
+ .exercises()
.iter()
- .zip(state_file.progress().iter().copied())
+ .zip(self.app_state.progress().iter().copied())
.enumerate()
.filter_map(|(ind, (exercise, done))| {
let exercise_state = if done {
- progress += 1;
-
if self.filter == Filter::Pending {
return None;
}
@@ -55,7 +54,7 @@ impl UiState {
rows_counter += 1;
- let next = if ind == state_file.next_exercise_ind() {
+ let next = if ind == current_exercise_ind {
">>>>".bold().red()
} else {
Span::default()
@@ -74,15 +73,14 @@ impl UiState {
self.last_ind = rows_counter.saturating_sub(1);
self.select(self.selected.min(self.last_ind));
- self.progress = progress;
-
self
}
- pub fn new(state_file: &StateFile, exercises: &'static [Exercise]) -> Self {
+ pub fn new(app_state: &'a mut AppState) -> Self {
let header = Row::new(["Next", "State", "Name", "Path"]);
- let max_name_len = exercises
+ let max_name_len = app_state
+ .exercises()
.iter()
.map(|exercise| exercise.name.len())
.max()
@@ -104,7 +102,7 @@ impl UiState {
.highlight_symbol("🦀")
.block(Block::default().borders(Borders::BOTTOM));
- let selected = state_file.next_exercise_ind();
+ let selected = app_state.current_exercise_ind();
let table_state = TableState::default()
.with_offset(selected.saturating_sub(10))
.with_selected(Some(selected));
@@ -113,19 +111,13 @@ impl UiState {
table,
message: String::with_capacity(128),
filter: Filter::None,
- exercises,
- progress: 0,
- selected,
+ app_state,
table_state,
+ selected,
last_ind: 0,
};
- slf.with_updated_rows(state_file)
- }
-
- #[inline]
- pub fn selected(&self) -> usize {
- self.selected
+ slf.with_updated_rows()
}
fn select(&mut self, ind: usize) {
@@ -134,11 +126,13 @@ impl UiState {
}
pub fn select_next(&mut self) {
- self.select(self.selected.saturating_add(1).min(self.last_ind));
+ let next = (self.selected + 1).min(self.last_ind);
+ self.select(next);
}
pub fn select_previous(&mut self) {
- self.select(self.selected.saturating_sub(1));
+ let previous = self.selected.saturating_sub(1);
+ self.select(previous);
}
#[inline]
@@ -167,8 +161,8 @@ impl UiState {
frame.render_widget(
Paragraph::new(progress_bar_ratatui(
- self.progress,
- self.exercises.len() as u16,
+ self.app_state.n_done(),
+ self.app_state.exercises().len() as u16,
area.width,
)?)
.block(Block::default().borders(Borders::BOTTOM)),
@@ -200,4 +194,19 @@ impl UiState {
Ok(())
}
+
+ pub fn reset_selected(&mut self) -> Result<&'static Exercise> {
+ self.app_state.set_pending(self.selected)?;
+ // TODO: Take care of filters!
+ let exercise = &self.app_state.exercises()[self.selected];
+ exercise.reset()?;
+
+ Ok(exercise)
+ }
+
+ #[inline]
+ pub fn selected_to_current_exercise(&mut self) -> Result<()> {
+ // TODO: Take care of filters!
+ self.app_state.set_current_exercise_ind(self.selected)
+ }
}
diff --git a/src/main.rs b/src/main.rs
index fc83e0f..926605c 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -1,7 +1,8 @@
-use anyhow::{bail, Context, Result};
+use anyhow::{Context, Result};
use clap::{Parser, Subcommand};
use std::{path::Path, process::exit};
+mod app_state;
mod consts;
mod embedded;
mod exercise;
@@ -9,17 +10,15 @@ mod init;
mod list;
mod progress_bar;
mod run;
-mod state_file;
-mod verify;
mod watch;
use self::{
+ app_state::AppState,
consts::WELCOME,
- exercise::{Exercise, InfoFile},
+ exercise::InfoFile,
+ init::init,
list::list,
run::run,
- state_file::StateFile,
- verify::{verify, VerifyState},
watch::{watch, WatchExit},
};
@@ -35,14 +34,12 @@ struct Args {
enum Subcommands {
/// Initialize Rustlings
Init,
- /// Verify all exercises according to the recommended order
- Verify,
/// Same as just running `rustlings` without a subcommand.
Watch,
- /// Run/Test a single exercise
+ /// Run a single exercise. Runs the next pending exercise if the exercise name is not specified.
Run {
/// The name of the exercise
- name: String,
+ name: Option<String>,
},
/// Reset a single exercise
Reset {
@@ -56,26 +53,6 @@ enum Subcommands {
},
}
-fn find_exercise(name: &str, exercises: &'static [Exercise]) -> Result<(usize, &'static Exercise)> {
- if name == "next" {
- for (ind, exercise) in exercises.iter().enumerate() {
- if !exercise.looks_done()? {
- return Ok((ind, exercise));
- }
- }
-
- println!("🎉 Congratulations! You have done all the exercises!");
- println!("🔚 There are no more exercises to do next!");
- exit(0);
- }
-
- exercises
- .iter()
- .enumerate()
- .find(|(_, exercise)| exercise.name == name)
- .with_context(|| format!("No exercise found for '{name}'!"))
-}
-
fn main() -> Result<()> {
let args = Args::parse();
@@ -87,11 +64,10 @@ Try running `cargo --version` to diagnose the problem.",
let mut info_file = InfoFile::parse()?;
info_file.exercises.shrink_to_fit();
- // Leaking is not a problem since the exercises' slice is used until the end of the program.
- let exercises = info_file.exercises.leak();
+ let exercises = info_file.exercises;
if matches!(args.command, Some(Subcommands::Init)) {
- init::init(exercises).context("Initialization failed")?;
+ init(&exercises).context("Initialization failed")?;
println!(
"\nDone initialization!\n
Run `cd rustlings` to go into the generated directory.
@@ -109,38 +85,37 @@ If you are just starting with Rustlings, run the command `rustlings init` to ini
exit(1);
}
- let mut state_file = StateFile::read_or_default(exercises);
+ let mut app_state = AppState::new(exercises);
match args.command {
None | Some(Subcommands::Watch) => loop {
- match watch(&mut state_file, exercises)? {
+ match watch(&mut app_state)? {
WatchExit::Shutdown => break,
// It is much easier to exit the watch mode, launch the list mode and then restart
// the watch mode instead of trying to pause the watch threads and correct the
// watch state.
- WatchExit::List => list(&mut state_file, exercises)?,
+ WatchExit::List => list(&mut app_state)?,
}
},
// `Init` is handled above.
Some(Subcommands::Init) => (),
Some(Subcommands::Run { name }) => {
- let (_, exercise) = find_exercise(&name, exercises)?;
- run(exercise).unwrap_or_else(|_| exit(1));
+ if let Some(name) = name {
+ app_state.set_current_exercise_by_name(&name)?;
+ }
+ run(&mut app_state)?;
}
Some(Subcommands::Reset { name }) => {
- let (ind, exercise) = find_exercise(&name, exercises)?;
+ app_state.set_current_exercise_by_name(&name)?;
+ app_state.set_pending(app_state.current_exercise_ind())?;
+ let exercise = app_state.current_exercise();
exercise.reset()?;
- state_file.reset(ind)?;
println!("The exercise {exercise} has been reset!");
}
Some(Subcommands::Hint { name }) => {
- let (_, exercise) = find_exercise(&name, exercises)?;
- println!("{}", exercise.hint);
+ app_state.set_current_exercise_by_name(&name)?;
+ println!("{}", app_state.current_exercise().hint);
}
- Some(Subcommands::Verify) => match verify(exercises, 0)? {
- VerifyState::AllExercisesDone => println!("All exercises done!"),
- VerifyState::Failed(exercise) => bail!("Exercise {exercise} failed"),
- },
}
Ok(())
diff --git a/src/run.rs b/src/run.rs
index 2fd6f40..18da193 100644
--- a/src/run.rs
+++ b/src/run.rs
@@ -2,13 +2,10 @@ use anyhow::{bail, Result};
use crossterm::style::Stylize;
use std::io::{stdout, Write};
-use crate::exercise::Exercise;
+use crate::app_state::{AppState, ExercisesProgress};
-// Invoke the rust compiler on the path of the given exercise,
-// and run the ensuing binary.
-// The verbose argument helps determine whether or not to show
-// the output from the test harnesses (if the mode of the exercise is test)
-pub fn run(exercise: &Exercise) -> Result<()> {
+pub fn run(app_state: &mut AppState) -> Result<()> {
+ let exercise = app_state.current_exercise();
let output = exercise.run()?;
{
@@ -22,7 +19,19 @@ pub fn run(exercise: &Exercise) -> Result<()> {
bail!("Ran {exercise} with errors");
}
- println!("{}", "✓ Successfully ran {exercise}".green());
+ println!(
+ "{}{}",
+ "✓ Successfully ran ".green(),
+ exercise.path.to_string_lossy().green(),
+ );
+
+ match app_state.done_current_exercise()? {
+ ExercisesProgress::AllDone => println!(
+ "🎉 Congratulations! You have done all the exercises!
+🔚 There are no more exercises to do next!"
+ ),
+ ExercisesProgress::Pending => println!("Next exercise: {}", app_state.current_exercise()),
+ }
Ok(())
}
diff --git a/src/state_file.rs b/src/state_file.rs
deleted file mode 100644
index 6b80354..0000000
--- a/src/state_file.rs
+++ /dev/null
@@ -1,68 +0,0 @@
-use anyhow::{bail, Context, Result};
-use serde::{Deserialize, Serialize};
-use std::fs;
-
-use crate::exercise::Exercise;
-
-#[derive(Serialize, Deserialize)]
-#[serde(deny_unknown_fields)]
-pub struct StateFile {
- next_exercise_ind: usize,
- progress: Vec<bool>,
-}
-
-const BAD_INDEX_ERR: &str = "The next exercise index is higher than the number of exercises";
-
-impl StateFile {
- fn read(exercises: &[Exercise]) -> Option<Self> {
- let file_content = fs::read(".rustlings-state.json").ok()?;
-
- let slf: Self = serde_json::de::from_slice(&file_content).ok()?;
-
- if slf.progress.len() != exercises.len() || slf.next_exercise_ind >= exercises.len() {
- return None;
- }
-
- Some(slf)
- }
-
- pub fn read_or_default(exercises: &[Exercise]) -> Self {
- Self::read(exercises).unwrap_or_else(|| Self {
- next_exercise_ind: 0,
- progress: vec![false; exercises.len()],
- })
- }
-
- fn write(&self) -> Result<()> {
- let mut buf = Vec::with_capacity(1024);
- serde_json::ser::to_writer(&mut buf, self).context("Failed to serialize the state")?;
- fs::write(".rustlings-state.json", buf)
- .context("Failed to write the state file `.rustlings-state.json`")?;
-
- Ok(())
- }
-
- #[inline]
- pub fn next_exercise_ind(&self) -> usize {
- self.next_exercise_ind
- }
-
- pub fn set_next_exercise_ind(&mut self, ind: usize) -> Result<()> {
- if ind >= self.progress.len() {
- bail!(BAD_INDEX_ERR);
- }
- self.next_exercise_ind = ind;
- self.write()
- }
-
- #[inline]
- pub fn progress(&self) -> &[bool] {
- &self.progress
- }
-
- pub fn reset(&mut self, ind: usize) -> Result<()> {
- let done = self.progress.get_mut(ind).context(BAD_INDEX_ERR)?;
- *done = false;
- self.write()
- }
-}
diff --git a/src/verify.rs b/src/verify.rs
deleted file mode 100644
index cea6bdf..0000000
--- a/src/verify.rs
+++ /dev/null
@@ -1,85 +0,0 @@
-use anyhow::Result;
-use crossterm::style::{Attribute, ContentStyle, Stylize};
-use std::io::{stdout, Write};
-
-use crate::exercise::{Exercise, Mode, State};
-
-pub enum VerifyState {
- AllExercisesDone,
- Failed(&'static Exercise),
-}
-
-// Verify that the provided container of Exercise objects
-// can be compiled and run without any failures.
-// Any such failures will be reported to the end user.
-// If the Exercise being verified is a test, the verbose boolean
-// determines whether or not the test harness outputs are displayed.
-pub fn verify(
- exercises: &'static [Exercise],
- mut current_exercise_ind: usize,
-) -> Result<VerifyState> {
- while current_exercise_ind < exercises.len() {
- let exercise = &exercises[current_exercise_ind];
-
- println!(
- "Progress: {current_exercise_ind}/{} ({:.1}%)\n",
- exercises.len(),
- current_exercise_ind as f32 / exercises.len() as f32 * 100.0,
- );
-
- let output = exercise.run()?;
-
- {
- let mut stdout = stdout().lock();
- stdout.write_all(&output.stdout)?;
- stdout.write_all(&output.stderr)?;
- stdout.flush()?;
- }
-
- if !output.status.success() {
- return Ok(VerifyState::Failed(exercise));
- }
-
- println!();
- // TODO: Color
- match exercise.mode {
- Mode::Compile => println!("Successfully ran {exercise}!"),
- Mode::Test => println!("Successfully tested {exercise}!"),
- Mode::Clippy => println!("Successfully checked {exercise}!"),
- }
-
- if let State::Pending(context) = exercise.state()? {
- println!(
- "\nYou can keep working on this exercise,
-or jump into the next one by removing the {} comment:\n",
- "`I AM NOT DONE`".bold()
- );
-
- for context_line in context {
- let formatted_line = if context_line.important {
- format!("{}", context_line.line.bold())
- } else {
- context_line.line
- };
-
- println!(
- "{:>2} {} {}",
- ContentStyle {
- foreground_color: Some(crossterm::style::Color::Blue),
- background_color: None,
- underline_color: None,
- attributes: Attribute::Bold.into()
- }
- .apply(context_line.number),
- "|".blue(),
- formatted_line,
- );
- }
- return Ok(VerifyState::Failed(exercise));
- }
-
- current_exercise_ind += 1;
- }
-
- Ok(VerifyState::AllExercisesDone)
-}
diff --git a/src/watch.rs b/src/watch.rs
index b29169b..929275f 100644
--- a/src/watch.rs
+++ b/src/watch.rs
@@ -15,7 +15,7 @@ mod debounce_event;
mod state;
mod terminal_event;
-use crate::{exercise::Exercise, state_file::StateFile};
+use crate::app_state::AppState;
use self::{
debounce_event::DebounceEventHandler,
@@ -39,23 +39,23 @@ pub enum WatchExit {
List,
}
-pub fn watch(state_file: &mut StateFile, exercises: &'static [Exercise]) -> Result<WatchExit> {
+pub fn watch(app_state: &mut AppState) -> Result<WatchExit> {
let (tx, rx) = channel();
let mut debouncer = new_debouncer(
Duration::from_secs(1),
DebounceEventHandler {
tx: tx.clone(),
- exercises,
+ exercises: app_state.exercises(),
},
)?;
debouncer
.watcher()
.watch(Path::new("exercises"), RecursiveMode::Recursive)?;
- let mut watch_state = WatchState::new(state_file, exercises);
+ let mut watch_state = WatchState::new(app_state);
// TODO: bool
- watch_state.run_exercise()?;
+ watch_state.run_current_exercise()?;
watch_state.render()?;
thread::spawn(move || terminal_event_handler(tx));
diff --git a/src/watch/state.rs b/src/watch/state.rs
index 6f6d2f1..a7647d8 100644
--- a/src/watch/state.rs
+++ b/src/watch/state.rs
@@ -1,26 +1,16 @@
-use anyhow::{Context, Result};
+use anyhow::Result;
use crossterm::{
- style::{Attribute, ContentStyle, Stylize},
+ style::Stylize,
terminal::{size, Clear, ClearType},
ExecutableCommand,
};
-use std::{
- fmt::Write as _,
- io::{self, StdoutLock, Write},
-};
+use std::io::{self, StdoutLock, Write};
-use crate::{
- exercise::{Exercise, State},
- progress_bar::progress_bar,
- state_file::StateFile,
-};
+use crate::{app_state::AppState, progress_bar::progress_bar};
pub struct WatchState<'a> {
writer: StdoutLock<'a>,
- exercises: &'static [Exercise],
- exercise: &'static Exercise,
- current_exercise_ind: usize,
- progress: u16,
+ app_state: &'a mut AppState,
stdout: Option<Vec<u8>>,
stderr: Option<Vec<u8>>,
message: Option<String>,
@@ -28,19 +18,12 @@ pub struct WatchState<'a> {
}
impl<'a> WatchState<'a> {
- pub fn new(state_file: &StateFile, exercises: &'static [Exercise]) -> Self {
- let current_exercise_ind = state_file.next_exercise_ind();
- let progress = state_file.progress().iter().filter(|done| **done).count() as u16;
- let exercise = &exercises[current_exercise_ind];
-
+ pub fn new(app_state: &'a mut AppState) -> Self {
let writer = io::stdout().lock();
Self {
writer,
- exercises,
- exercise,
- current_exercise_ind,
- progress,
+ app_state,
stdout: None,
stderr: None,
message: None,
@@ -53,8 +36,8 @@ impl<'a> WatchState<'a> {
self.writer
}
- pub fn run_exercise(&mut self) -> Result<bool> {
- let output = self.exercise.run()?;
+ pub fn run_current_exercise(&mut self) -> Result<bool> {
+ let output = self.app_state.current_exercise().run()?;
self.stdout = Some(output.stdout);
if !output.status.success() {
@@ -64,55 +47,15 @@ impl<'a> WatchState<'a> {
self.stderr = None;
- if let State::Pending(context) = self.exercise.state()? {
- let mut message = format!(
- "
-You can keep working on this exercise or jump into the next one by removing the {} comment:
-
-",
- "`I AM NOT DONE`".bold(),
- );
-
- for context_line in context {
- let formatted_line = if context_line.important {
- context_line.line.bold()
- } else {
- context_line.line.stylize()
- };
-
- writeln!(
- message,
- "{:>2} {} {}",
- ContentStyle {
- foreground_color: Some(crossterm::style::Color::Blue),
- background_color: None,
- underline_color: None,
- attributes: Attribute::Bold.into()
- }
- .apply(context_line.number),
- "|".blue(),
- formatted_line,
- )?;
- }
-
- self.message = Some(message);
- return Ok(false);
- }
-
Ok(true)
}
pub fn run_exercise_with_ind(&mut self, exercise_ind: usize) -> Result<bool> {
- self.exercise = self
- .exercises
- .get(exercise_ind)
- .context("Invalid exercise index")?;
- self.current_exercise_ind = exercise_ind;
-
- self.run_exercise()
+ self.app_state.set_current_exercise_ind(exercise_ind)?;
+ self.run_current_exercise()
}
- pub fn show_prompt(&mut self) -> io::Result<()> {
+ fn show_prompt(&mut self) -> io::Result<()> {
self.writer.write_all(b"\n\n")?;
if !self.hint_displayed {
@@ -150,18 +93,27 @@ You can keep working on this exercise or jump into the next one by removing the
if self.hint_displayed {
self.writer
.write_fmt(format_args!("\n{}\n", "Hint".bold().cyan().underlined()))?;
- self.writer.write_all(self.exercise.hint.as_bytes())?;
+ self.writer
+ .write_all(self.app_state.current_exercise().hint.as_bytes())?;
self.writer.write_all(b"\n\n")?;
}
let line_width = size()?.0;
- let progress_bar = progress_bar(self.progress, self.exercises.len() as u16, line_width)?;
+ let progress_bar = progress_bar(
+ self.app_state.n_done(),
+ self.app_state.exercises().len() as u16,
+ line_width,
+ )?;
self.writer.write_all(progress_bar.as_bytes())?;
self.writer.write_all(b"Current exercise: ")?;
self.writer.write_fmt(format_args!(
"{}",
- self.exercise.path.to_string_lossy().bold()
+ self.app_state
+ .current_exercise()
+ .path
+ .to_string_lossy()
+ .bold(),
))?;
self.show_prompt()?;
diff --git a/tests/fixture/state/exercises/pending_exercise.rs b/tests/fixture/state/exercises/pending_exercise.rs
index f579d0b..016b827 100644
--- a/tests/fixture/state/exercises/pending_exercise.rs
+++ b/tests/fixture/state/exercises/pending_exercise.rs
@@ -1,7 +1,5 @@
// fake_exercise
-// I AM NOT DONE
-
fn main() {
}
diff --git a/tests/fixture/state/exercises/pending_test_exercise.rs b/tests/fixture/state/exercises/pending_test_exercise.rs
index 8756f02..2002ef1 100644
--- a/tests/fixture/state/exercises/pending_test_exercise.rs
+++ b/tests/fixture/state/exercises/pending_test_exercise.rs
@@ -1,4 +1,2 @@
-// I AM NOT DONE
-
#[test]
fn it_works() {}
diff --git a/tests/integration_tests.rs b/tests/integration_tests.rs
index f8f4383..51cdefb 100644
--- a/tests/integration_tests.rs
+++ b/tests/integration_tests.rs
@@ -1,7 +1,6 @@
use assert_cmd::prelude::*;
-use glob::glob;
use predicates::boolean::PredicateBooleanExt;
-use std::{fs::File, io::Read, process::Command};
+use std::process::Command;
#[test]
fn fails_when_in_wrong_dir() {
@@ -138,31 +137,6 @@ fn get_hint_for_single_test() {
}
#[test]
-fn all_exercises_require_confirmation() {
- for exercise in glob("exercises/**/*.rs").unwrap() {
- let path = exercise.unwrap();
- if path.file_name().unwrap() == "mod.rs" {
- continue;
- }
- let source = {
- let mut file = File::open(&path).unwrap();
- let mut s = String::new();
- file.read_to_string(&mut s).unwrap();
- s
- };
- source
- .matches("// I AM NOT DONE")
- .next()
- .unwrap_or_else(|| {
- panic!(
- "There should be an `I AM NOT DONE` annotation in {:?}",
- path
- )
- });
- }
-}
-
-#[test]
fn run_compile_exercise_does_not_prompt() {
Command::cargo_bin("rustlings")
.unwrap()