From 79ca821e26711123c959e919eed2a630fa102cd5 Mon Sep 17 00:00:00 2001 From: mo8it Date: Sat, 30 Mar 2024 20:48:30 +0100 Subject: Fix tests --- tests/fixture/failure/compFailure.rs | 3 --- tests/fixture/failure/compNoExercise.rs | 2 -- tests/fixture/failure/exercises/compFailure.rs | 3 +++ tests/fixture/failure/exercises/compNoExercise.rs | 2 ++ tests/fixture/failure/exercises/testFailure.rs | 4 ++++ tests/fixture/failure/exercises/testNotPassed.rs | 4 ++++ tests/fixture/failure/info.toml | 4 ++-- tests/fixture/failure/testFailure.rs | 4 ---- tests/fixture/failure/testNotPassed.rs | 4 ---- tests/fixture/state/exercises/finished_exercise.rs | 5 +++++ tests/fixture/state/exercises/pending_exercise.rs | 7 +++++++ tests/fixture/state/exercises/pending_test_exercise.rs | 4 ++++ tests/fixture/state/finished_exercise.rs | 5 ----- tests/fixture/state/info.toml | 7 +++---- tests/fixture/state/pending_exercise.rs | 7 ------- tests/fixture/state/pending_test_exercise.rs | 4 ---- tests/fixture/success/compSuccess.rs | 2 -- tests/fixture/success/exercises/compSuccess.rs | 2 ++ tests/fixture/success/exercises/testSuccess.rs | 5 +++++ tests/fixture/success/info.toml | 4 ++-- tests/fixture/success/testSuccess.rs | 5 ----- 21 files changed, 43 insertions(+), 44 deletions(-) delete mode 100644 tests/fixture/failure/compFailure.rs delete mode 100644 tests/fixture/failure/compNoExercise.rs create mode 100644 tests/fixture/failure/exercises/compFailure.rs create mode 100644 tests/fixture/failure/exercises/compNoExercise.rs create mode 100644 tests/fixture/failure/exercises/testFailure.rs create mode 100644 tests/fixture/failure/exercises/testNotPassed.rs delete mode 100644 tests/fixture/failure/testFailure.rs delete mode 100644 tests/fixture/failure/testNotPassed.rs create mode 100644 tests/fixture/state/exercises/finished_exercise.rs create mode 100644 tests/fixture/state/exercises/pending_exercise.rs create mode 100644 tests/fixture/state/exercises/pending_test_exercise.rs delete mode 100644 tests/fixture/state/finished_exercise.rs delete mode 100644 tests/fixture/state/pending_exercise.rs delete mode 100644 tests/fixture/state/pending_test_exercise.rs delete mode 100644 tests/fixture/success/compSuccess.rs create mode 100644 tests/fixture/success/exercises/compSuccess.rs create mode 100644 tests/fixture/success/exercises/testSuccess.rs delete mode 100644 tests/fixture/success/testSuccess.rs (limited to 'tests/fixture') diff --git a/tests/fixture/failure/compFailure.rs b/tests/fixture/failure/compFailure.rs deleted file mode 100644 index 566856a..0000000 --- a/tests/fixture/failure/compFailure.rs +++ /dev/null @@ -1,3 +0,0 @@ -fn main() { - let -} \ No newline at end of file diff --git a/tests/fixture/failure/compNoExercise.rs b/tests/fixture/failure/compNoExercise.rs deleted file mode 100644 index f79c691..0000000 --- a/tests/fixture/failure/compNoExercise.rs +++ /dev/null @@ -1,2 +0,0 @@ -fn main() { -} diff --git a/tests/fixture/failure/exercises/compFailure.rs b/tests/fixture/failure/exercises/compFailure.rs new file mode 100644 index 0000000..566856a --- /dev/null +++ b/tests/fixture/failure/exercises/compFailure.rs @@ -0,0 +1,3 @@ +fn main() { + let +} \ No newline at end of file diff --git a/tests/fixture/failure/exercises/compNoExercise.rs b/tests/fixture/failure/exercises/compNoExercise.rs new file mode 100644 index 0000000..f79c691 --- /dev/null +++ b/tests/fixture/failure/exercises/compNoExercise.rs @@ -0,0 +1,2 @@ +fn main() { +} diff --git a/tests/fixture/failure/exercises/testFailure.rs b/tests/fixture/failure/exercises/testFailure.rs new file mode 100644 index 0000000..b33a5d2 --- /dev/null +++ b/tests/fixture/failure/exercises/testFailure.rs @@ -0,0 +1,4 @@ +#[test] +fn passing() { + asset!(true); +} diff --git a/tests/fixture/failure/exercises/testNotPassed.rs b/tests/fixture/failure/exercises/testNotPassed.rs new file mode 100644 index 0000000..a9fe88d --- /dev/null +++ b/tests/fixture/failure/exercises/testNotPassed.rs @@ -0,0 +1,4 @@ +#[test] +fn not_passing() { + assert!(false); +} diff --git a/tests/fixture/failure/info.toml b/tests/fixture/failure/info.toml index e5949f9..9474ee3 100644 --- a/tests/fixture/failure/info.toml +++ b/tests/fixture/failure/info.toml @@ -1,11 +1,11 @@ [[exercises]] name = "compFailure" -path = "compFailure.rs" +path = "exercises/compFailure.rs" mode = "compile" hint = "" [[exercises]] name = "testFailure" -path = "testFailure.rs" +path = "exercises/testFailure.rs" mode = "test" hint = "Hello!" diff --git a/tests/fixture/failure/testFailure.rs b/tests/fixture/failure/testFailure.rs deleted file mode 100644 index b33a5d2..0000000 --- a/tests/fixture/failure/testFailure.rs +++ /dev/null @@ -1,4 +0,0 @@ -#[test] -fn passing() { - asset!(true); -} diff --git a/tests/fixture/failure/testNotPassed.rs b/tests/fixture/failure/testNotPassed.rs deleted file mode 100644 index a9fe88d..0000000 --- a/tests/fixture/failure/testNotPassed.rs +++ /dev/null @@ -1,4 +0,0 @@ -#[test] -fn not_passing() { - assert!(false); -} diff --git a/tests/fixture/state/exercises/finished_exercise.rs b/tests/fixture/state/exercises/finished_exercise.rs new file mode 100644 index 0000000..016b827 --- /dev/null +++ b/tests/fixture/state/exercises/finished_exercise.rs @@ -0,0 +1,5 @@ +// fake_exercise + +fn main() { + +} diff --git a/tests/fixture/state/exercises/pending_exercise.rs b/tests/fixture/state/exercises/pending_exercise.rs new file mode 100644 index 0000000..f579d0b --- /dev/null +++ b/tests/fixture/state/exercises/pending_exercise.rs @@ -0,0 +1,7 @@ +// 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 new file mode 100644 index 0000000..8756f02 --- /dev/null +++ b/tests/fixture/state/exercises/pending_test_exercise.rs @@ -0,0 +1,4 @@ +// I AM NOT DONE + +#[test] +fn it_works() {} diff --git a/tests/fixture/state/finished_exercise.rs b/tests/fixture/state/finished_exercise.rs deleted file mode 100644 index 016b827..0000000 --- a/tests/fixture/state/finished_exercise.rs +++ /dev/null @@ -1,5 +0,0 @@ -// fake_exercise - -fn main() { - -} diff --git a/tests/fixture/state/info.toml b/tests/fixture/state/info.toml index 547b3a4..8de5d60 100644 --- a/tests/fixture/state/info.toml +++ b/tests/fixture/state/info.toml @@ -1,18 +1,17 @@ [[exercises]] name = "pending_exercise" -path = "pending_exercise.rs" +path = "exercises/pending_exercise.rs" mode = "compile" hint = """""" [[exercises]] name = "pending_test_exercise" -path = "pending_test_exercise.rs" +path = "exercises/pending_test_exercise.rs" mode = "test" hint = """""" [[exercises]] name = "finished_exercise" -path = "finished_exercise.rs" +path = "exercises/finished_exercise.rs" mode = "compile" hint = """""" - diff --git a/tests/fixture/state/pending_exercise.rs b/tests/fixture/state/pending_exercise.rs deleted file mode 100644 index f579d0b..0000000 --- a/tests/fixture/state/pending_exercise.rs +++ /dev/null @@ -1,7 +0,0 @@ -// fake_exercise - -// I AM NOT DONE - -fn main() { - -} diff --git a/tests/fixture/state/pending_test_exercise.rs b/tests/fixture/state/pending_test_exercise.rs deleted file mode 100644 index 8756f02..0000000 --- a/tests/fixture/state/pending_test_exercise.rs +++ /dev/null @@ -1,4 +0,0 @@ -// I AM NOT DONE - -#[test] -fn it_works() {} diff --git a/tests/fixture/success/compSuccess.rs b/tests/fixture/success/compSuccess.rs deleted file mode 100644 index f79c691..0000000 --- a/tests/fixture/success/compSuccess.rs +++ /dev/null @@ -1,2 +0,0 @@ -fn main() { -} diff --git a/tests/fixture/success/exercises/compSuccess.rs b/tests/fixture/success/exercises/compSuccess.rs new file mode 100644 index 0000000..f79c691 --- /dev/null +++ b/tests/fixture/success/exercises/compSuccess.rs @@ -0,0 +1,2 @@ +fn main() { +} diff --git a/tests/fixture/success/exercises/testSuccess.rs b/tests/fixture/success/exercises/testSuccess.rs new file mode 100644 index 0000000..7139b50 --- /dev/null +++ b/tests/fixture/success/exercises/testSuccess.rs @@ -0,0 +1,5 @@ +#[test] +fn passing() { + println!("THIS TEST TOO SHALL PASS"); + assert!(true); +} diff --git a/tests/fixture/success/info.toml b/tests/fixture/success/info.toml index 68d3538..17ed8c6 100644 --- a/tests/fixture/success/info.toml +++ b/tests/fixture/success/info.toml @@ -1,11 +1,11 @@ [[exercises]] name = "compSuccess" -path = "compSuccess.rs" +path = "exercises/compSuccess.rs" mode = "compile" hint = """""" [[exercises]] name = "testSuccess" -path = "testSuccess.rs" +path = "exercises/testSuccess.rs" mode = "test" hint = """""" diff --git a/tests/fixture/success/testSuccess.rs b/tests/fixture/success/testSuccess.rs deleted file mode 100644 index 7139b50..0000000 --- a/tests/fixture/success/testSuccess.rs +++ /dev/null @@ -1,5 +0,0 @@ -#[test] -fn passing() { - println!("THIS TEST TOO SHALL PASS"); - assert!(true); -} -- cgit v1.2.3 From 7090fffeae88a2afdeb42ae3301c4842416ab729 Mon Sep 17 00:00:00 2001 From: mo8it Date: Sun, 31 Mar 2024 18:59:01 +0200 Subject: Fix tests --- .gitignore | 7 +++---- Cargo.toml | 5 +++++ tests/fixture/failure/Cargo.toml | 21 +++++++++++++++++++++ tests/fixture/state/Cargo.toml | 17 +++++++++++++++++ tests/fixture/success/Cargo.toml | 13 +++++++++++++ 5 files changed, 59 insertions(+), 4 deletions(-) create mode 100644 tests/fixture/failure/Cargo.toml create mode 100644 tests/fixture/state/Cargo.toml create mode 100644 tests/fixture/success/Cargo.toml (limited to 'tests/fixture') diff --git a/.gitignore b/.gitignore index f319d39..d6c7708 100644 --- a/.gitignore +++ b/.gitignore @@ -1,11 +1,10 @@ -*.swp target/ +tests/fixture/*/Cargo.lock + +*.swp **/*.rs.bk .DS_Store *.pdb -exercises/clippy/Cargo.toml -exercises/clippy/Cargo.lock -rust-project.json .idea .vscode/* !.vscode/extensions.json diff --git a/Cargo.toml b/Cargo.toml index 9224364..5fc75f9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,5 +1,10 @@ [workspace] resolver = "2" +exclude = [ + "tests/fixture/failure", + "tests/fixture/state", + "tests/fixture/success", +] [workspace.package] version = "6.0.0" diff --git a/tests/fixture/failure/Cargo.toml b/tests/fixture/failure/Cargo.toml new file mode 100644 index 0000000..dd728c3 --- /dev/null +++ b/tests/fixture/failure/Cargo.toml @@ -0,0 +1,21 @@ +[package] +name = "tests" +version = "0.0.0" +edition = "2021" +publish = false + +[[bin]] +name = "compFailure" +path = "exercises/compFailure.rs" + +[[bin]] +name = "compNoExercise" +path = "exercises/compNoExercise.rs" + +[[bin]] +name = "testFailure" +path = "exercises/testFailure.rs" + +[[bin]] +name = "testNotPassed" +path = "exercises/testNotPassed.rs" diff --git a/tests/fixture/state/Cargo.toml b/tests/fixture/state/Cargo.toml new file mode 100644 index 0000000..5cfa42b --- /dev/null +++ b/tests/fixture/state/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "tests" +version = "0.0.0" +edition = "2021" +publish = false + +[[bin]] +name = "pending_exercise" +path = "exercises/pending_exercise.rs" + +[[bin]] +name = "pending_test_exercise" +path = "exercises/pending_test_exercise.rs" + +[[bin]] +name = "finished_exercise" +path = "exercises/finished_exercise.rs" diff --git a/tests/fixture/success/Cargo.toml b/tests/fixture/success/Cargo.toml new file mode 100644 index 0000000..c005928 --- /dev/null +++ b/tests/fixture/success/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "tests" +version = "0.0.0" +edition = "2021" +publish = false + +[[bin]] +name = "compSuccess" +path = "exercises/compSuccess.rs" + +[[bin]] +name = "testSuccess" +path = "exercises/testSuccess.rs" -- cgit v1.2.3 From b6c434c445d91a9e886e5639b078635e5eca4eb3 Mon Sep 17 00:00:00 2001 From: mo8it Date: Thu, 4 Apr 2024 15:45:53 +0200 Subject: Remove optional version field --- dev/Cargo.toml | 1 - src/bin/gen-dev-cargo-toml.rs | 1 - src/init.rs | 1 - tests/fixture/failure/Cargo.toml | 1 - tests/fixture/state/Cargo.toml | 1 - tests/fixture/success/Cargo.toml | 1 - 6 files changed, 6 deletions(-) (limited to 'tests/fixture') diff --git a/dev/Cargo.toml b/dev/Cargo.toml index e4e7be7..7868b97 100644 --- a/dev/Cargo.toml +++ b/dev/Cargo.toml @@ -102,6 +102,5 @@ bin = [ [package] name = "rustlings" -version = "0.0.0" edition = "2021" publish = false diff --git a/src/bin/gen-dev-cargo-toml.rs b/src/bin/gen-dev-cargo-toml.rs index 65cc244..ff8f31d 100644 --- a/src/bin/gen-dev-cargo-toml.rs +++ b/src/bin/gen-dev-cargo-toml.rs @@ -49,7 +49,6 @@ bin = [\n", [package] name = "rustlings" -version = "0.0.0" edition = "2021" publish = false "#, diff --git a/src/init.rs b/src/init.rs index b52b613..6af3235 100644 --- a/src/init.rs +++ b/src/init.rs @@ -24,7 +24,6 @@ fn create_cargo_toml(exercises: &[Exercise]) -> io::Result<()> { [package] name = "rustlings" -version = "0.0.0" edition = "2021" publish = false "#, diff --git a/tests/fixture/failure/Cargo.toml b/tests/fixture/failure/Cargo.toml index dd728c3..e111cf2 100644 --- a/tests/fixture/failure/Cargo.toml +++ b/tests/fixture/failure/Cargo.toml @@ -1,6 +1,5 @@ [package] name = "tests" -version = "0.0.0" edition = "2021" publish = false diff --git a/tests/fixture/state/Cargo.toml b/tests/fixture/state/Cargo.toml index 5cfa42b..c8d74e4 100644 --- a/tests/fixture/state/Cargo.toml +++ b/tests/fixture/state/Cargo.toml @@ -1,6 +1,5 @@ [package] name = "tests" -version = "0.0.0" edition = "2021" publish = false diff --git a/tests/fixture/success/Cargo.toml b/tests/fixture/success/Cargo.toml index c005928..f26a44f 100644 --- a/tests/fixture/success/Cargo.toml +++ b/tests/fixture/success/Cargo.toml @@ -1,6 +1,5 @@ [package] name = "tests" -version = "0.0.0" edition = "2021" publish = false -- cgit v1.2.3 From 60155294e94acd661e4fe20cf8b72412167c772d Mon Sep 17 00:00:00 2001 From: mo8it Date: Sat, 6 Apr 2024 01:45:54 +0200 Subject: Rename packages --- dev/Cargo.toml | 2 +- gen-dev-cargo-toml/src/main.rs | 2 +- tests/fixture/failure/Cargo.toml | 2 +- tests/fixture/state/Cargo.toml | 2 +- tests/fixture/success/Cargo.toml | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) (limited to 'tests/fixture') diff --git a/dev/Cargo.toml b/dev/Cargo.toml index ed9b3ed..1d230eb 100644 --- a/dev/Cargo.toml +++ b/dev/Cargo.toml @@ -101,6 +101,6 @@ bin = [ ] [package] -name = "rustlings" +name = "rustlings-dev" edition = "2021" publish = false diff --git a/gen-dev-cargo-toml/src/main.rs b/gen-dev-cargo-toml/src/main.rs index 622762a..9a7c1bb 100644 --- a/gen-dev-cargo-toml/src/main.rs +++ b/gen-dev-cargo-toml/src/main.rs @@ -48,7 +48,7 @@ bin = [\n", br#"] [package] -name = "rustlings" +name = "rustlings-dev" edition = "2021" publish = false "#, diff --git a/tests/fixture/failure/Cargo.toml b/tests/fixture/failure/Cargo.toml index e111cf2..7ee2f06 100644 --- a/tests/fixture/failure/Cargo.toml +++ b/tests/fixture/failure/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "tests" +name = "failure" edition = "2021" publish = false diff --git a/tests/fixture/state/Cargo.toml b/tests/fixture/state/Cargo.toml index c8d74e4..adbd8ab 100644 --- a/tests/fixture/state/Cargo.toml +++ b/tests/fixture/state/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "tests" +name = "state" edition = "2021" publish = false diff --git a/tests/fixture/success/Cargo.toml b/tests/fixture/success/Cargo.toml index f26a44f..028cf35 100644 --- a/tests/fixture/success/Cargo.toml +++ b/tests/fixture/success/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "tests" +name = "success" edition = "2021" publish = false -- cgit v1.2.3 From fa1f239a702eb2c0b7e0115e986481156961bbc8 Mon Sep 17 00:00:00 2001 From: mo8it Date: Thu, 11 Apr 2024 02:51:02 +0200 Subject: Remove "I AM NOT DONE" and the verify mode and add AppState --- Cargo.lock | 1 - Cargo.toml | 1 - README.md | 8 +- exercises/00_intro/intro1.rs | 4 +- exercises/00_intro/intro2.rs | 2 - exercises/01_variables/variables1.rs | 2 - exercises/01_variables/variables2.rs | 2 - exercises/01_variables/variables3.rs | 2 - exercises/01_variables/variables4.rs | 2 - exercises/01_variables/variables5.rs | 2 - exercises/01_variables/variables6.rs | 2 - exercises/02_functions/functions1.rs | 2 - exercises/02_functions/functions2.rs | 2 - exercises/02_functions/functions3.rs | 2 - exercises/02_functions/functions4.rs | 2 - exercises/02_functions/functions5.rs | 2 - exercises/03_if/if1.rs | 2 - exercises/03_if/if2.rs | 2 - exercises/03_if/if3.rs | 2 - exercises/04_primitive_types/primitive_types1.rs | 2 - exercises/04_primitive_types/primitive_types2.rs | 2 - exercises/04_primitive_types/primitive_types3.rs | 2 - exercises/04_primitive_types/primitive_types4.rs | 2 - exercises/04_primitive_types/primitive_types5.rs | 2 - exercises/04_primitive_types/primitive_types6.rs | 2 - exercises/05_vecs/vecs1.rs | 2 - exercises/05_vecs/vecs2.rs | 2 - exercises/06_move_semantics/move_semantics1.rs | 2 - exercises/06_move_semantics/move_semantics2.rs | 2 - exercises/06_move_semantics/move_semantics3.rs | 2 - exercises/06_move_semantics/move_semantics4.rs | 2 - exercises/06_move_semantics/move_semantics5.rs | 2 - exercises/06_move_semantics/move_semantics6.rs | 2 - exercises/07_structs/structs1.rs | 2 - exercises/07_structs/structs2.rs | 2 - exercises/07_structs/structs3.rs | 2 - exercises/08_enums/enums1.rs | 2 - exercises/08_enums/enums2.rs | 2 - exercises/08_enums/enums3.rs | 2 - exercises/09_strings/strings1.rs | 2 - exercises/09_strings/strings2.rs | 2 - exercises/09_strings/strings3.rs | 2 - exercises/09_strings/strings4.rs | 2 - exercises/10_modules/modules1.rs | 2 - exercises/10_modules/modules2.rs | 2 - exercises/10_modules/modules3.rs | 2 - exercises/11_hashmaps/hashmaps1.rs | 2 - exercises/11_hashmaps/hashmaps2.rs | 2 - exercises/11_hashmaps/hashmaps3.rs | 2 - exercises/12_options/options1.rs | 2 - exercises/12_options/options2.rs | 2 - exercises/12_options/options3.rs | 2 - exercises/13_error_handling/errors1.rs | 2 - exercises/13_error_handling/errors2.rs | 2 - exercises/13_error_handling/errors3.rs | 2 - exercises/13_error_handling/errors4.rs | 2 - exercises/13_error_handling/errors5.rs | 2 - exercises/13_error_handling/errors6.rs | 2 - exercises/14_generics/generics1.rs | 2 - exercises/14_generics/generics2.rs | 2 - exercises/15_traits/traits1.rs | 2 - exercises/15_traits/traits2.rs | 2 - exercises/15_traits/traits3.rs | 2 - exercises/15_traits/traits4.rs | 2 - exercises/15_traits/traits5.rs | 2 - exercises/16_lifetimes/lifetimes1.rs | 2 - exercises/16_lifetimes/lifetimes2.rs | 2 - exercises/16_lifetimes/lifetimes3.rs | 2 - exercises/17_tests/tests1.rs | 2 - exercises/17_tests/tests2.rs | 2 - exercises/17_tests/tests3.rs | 2 - exercises/17_tests/tests4.rs | 2 - exercises/18_iterators/iterators1.rs | 2 - exercises/18_iterators/iterators2.rs | 2 - exercises/18_iterators/iterators3.rs | 2 - exercises/18_iterators/iterators4.rs | 2 - exercises/18_iterators/iterators5.rs | 2 - exercises/19_smart_pointers/arc1.rs | 2 - exercises/19_smart_pointers/box1.rs | 2 - exercises/19_smart_pointers/cow1.rs | 2 - exercises/19_smart_pointers/rc1.rs | 2 - exercises/20_threads/threads1.rs | 2 - exercises/20_threads/threads2.rs | 2 - exercises/20_threads/threads3.rs | 2 - exercises/21_macros/macros1.rs | 2 - exercises/21_macros/macros2.rs | 2 - exercises/21_macros/macros3.rs | 2 - exercises/21_macros/macros4.rs | 2 - exercises/22_clippy/clippy1.rs | 2 - exercises/22_clippy/clippy2.rs | 2 - exercises/22_clippy/clippy3.rs | 2 - exercises/23_conversions/as_ref_mut.rs | 2 - exercises/23_conversions/from_into.rs | 2 - exercises/23_conversions/from_str.rs | 2 - exercises/23_conversions/try_from_into.rs | 2 - exercises/23_conversions/using_as.rs | 2 - exercises/quiz1.rs | 2 - exercises/quiz2.rs | 2 - exercises/quiz3.rs | 2 - info.toml | 7 +- src/app_state.rs | 185 ++++++++++++++++++ src/exercise.rs | 206 +-------------------- src/list.rs | 21 +-- src/list/state.rs | 71 +++---- src/main.rs | 67 +++---- src/run.rs | 23 ++- src/state_file.rs | 68 ------- src/verify.rs | 85 --------- src/watch.rs | 10 +- src/watch/state.rs | 96 +++------- tests/fixture/state/exercises/pending_exercise.rs | 2 - .../state/exercises/pending_test_exercise.rs | 2 - tests/integration_tests.rs | 28 +-- 113 files changed, 306 insertions(+), 769 deletions(-) create mode 100644 src/app_state.rs delete mode 100644 src/state_file.rs delete mode 100644 src/verify.rs (limited to 'tests/fixture') 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) { 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) -> Vec { 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 { 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 { 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 { 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(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::(); 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, +} + +impl StateFile { + fn read(exercises: &[Exercise]) -> Option { + 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) -> 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 { + 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 { + 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), -} - // 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 { - 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 { - 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, }, /// 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, -} - -const BAD_INDEX_ERR: &str = "The next exercise index is higher than the number of exercises"; - -impl StateFile { - fn read(exercises: &[Exercise]) -> Option { - 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 { - 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 { +pub fn watch(app_state: &mut AppState) -> Result { 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>, stderr: Option>, message: Option, @@ -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 { - let output = self.exercise.run()?; + pub fn run_current_exercise(&mut self) -> Result { + 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 { - 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() { @@ -137,31 +136,6 @@ fn get_hint_for_single_test() { .stdout("Hello!\n"); } -#[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") -- cgit v1.2.3 From 9831cbb13975cd0f5ee4c295156102e3573ede1a Mon Sep 17 00:00:00 2001 From: mo8it Date: Sun, 14 Apr 2024 03:13:33 +0200 Subject: Fix tests --- gen-dev-cargo-toml/src/main.rs | 24 ++++++++++++++---------- tests/dev_cargo_bins.rs | 39 ++++++++++++++++++++++----------------- tests/fixture/failure/info.toml | 4 +--- tests/fixture/state/info.toml | 7 ++----- tests/fixture/success/info.toml | 4 +--- 5 files changed, 40 insertions(+), 38 deletions(-) (limited to 'tests/fixture') diff --git a/gen-dev-cargo-toml/src/main.rs b/gen-dev-cargo-toml/src/main.rs index 9a7c1bb..792fe5f 100644 --- a/gen-dev-cargo-toml/src/main.rs +++ b/gen-dev-cargo-toml/src/main.rs @@ -10,18 +10,18 @@ use std::{ }; #[derive(Deserialize)] -struct Exercise { +struct ExerciseInfo { name: String, - path: String, + dir: Option, } #[derive(Deserialize)] -struct InfoToml { - exercises: Vec, +struct InfoFile { + exercises: Vec, } fn main() -> Result<()> { - let exercises = toml_edit::de::from_str::( + let exercise_infos = toml_edit::de::from_str::( &fs::read_to_string("info.toml").context("Failed to read `info.toml`")?, ) .context("Failed to deserialize `info.toml`")? @@ -36,12 +36,16 @@ fn main() -> Result<()> { bin = [\n", ); - for exercise in exercises { + for exercise_info in exercise_infos { buf.extend_from_slice(b" { name = \""); - buf.extend_from_slice(exercise.name.as_bytes()); - buf.extend_from_slice(b"\", path = \"../"); - buf.extend_from_slice(exercise.path.as_bytes()); - buf.extend_from_slice(b"\" },\n"); + buf.extend_from_slice(exercise_info.name.as_bytes()); + buf.extend_from_slice(b"\", path = \"../exercises/"); + if let Some(dir) = &exercise_info.dir { + buf.extend_from_slice(dir.as_bytes()); + buf.extend_from_slice(b"/"); + } + buf.extend_from_slice(exercise_info.name.as_bytes()); + buf.extend_from_slice(b".rs\" },\n"); } buf.extend_from_slice( diff --git a/tests/dev_cargo_bins.rs b/tests/dev_cargo_bins.rs index c3faea9..81f48b1 100644 --- a/tests/dev_cargo_bins.rs +++ b/tests/dev_cargo_bins.rs @@ -5,34 +5,39 @@ use serde::Deserialize; use std::fs; #[derive(Deserialize)] -struct Exercise { +struct ExerciseInfo { name: String, - path: String, + dir: Option, } #[derive(Deserialize)] -struct InfoToml { - exercises: Vec, +struct InfoFile { + exercises: Vec, } #[test] fn dev_cargo_bins() { - let content = fs::read_to_string("dev/Cargo.toml").unwrap(); + let cargo_toml = fs::read_to_string("dev/Cargo.toml").unwrap(); - let exercises = toml_edit::de::from_str::(&fs::read_to_string("info.toml").unwrap()) - .unwrap() - .exercises; + let exercise_infos = + toml_edit::de::from_str::(&fs::read_to_string("info.toml").unwrap()) + .unwrap() + .exercises; let mut start_ind = 0; - for exercise in exercises { - let name_start = start_ind + content[start_ind..].find('"').unwrap() + 1; - let name_end = name_start + content[name_start..].find('"').unwrap(); - assert_eq!(exercise.name, &content[name_start..name_end]); - - // +3 to skip `../` at the begeinning of the path. - let path_start = name_end + content[name_end + 1..].find('"').unwrap() + 5; - let path_end = path_start + content[path_start..].find('"').unwrap(); - assert_eq!(exercise.path, &content[path_start..path_end]); + for exercise_info in exercise_infos { + let name_start = start_ind + cargo_toml[start_ind..].find('"').unwrap() + 1; + let name_end = name_start + cargo_toml[name_start..].find('"').unwrap(); + assert_eq!(exercise_info.name, &cargo_toml[name_start..name_end]); + + let path_start = name_end + cargo_toml[name_end + 1..].find('"').unwrap() + 2; + let path_end = path_start + cargo_toml[path_start..].find('"').unwrap(); + let expected_path = if let Some(dir) = exercise_info.dir { + format!("../exercises/{dir}/{}.rs", exercise_info.name) + } else { + format!("../exercises/{}.rs", exercise_info.name) + }; + assert_eq!(expected_path, &cargo_toml[path_start..path_end]); start_ind = path_end + 1; } diff --git a/tests/fixture/failure/info.toml b/tests/fixture/failure/info.toml index 9474ee3..94ec6ea 100644 --- a/tests/fixture/failure/info.toml +++ b/tests/fixture/failure/info.toml @@ -1,11 +1,9 @@ [[exercises]] name = "compFailure" -path = "exercises/compFailure.rs" -mode = "compile" +mode = "run" hint = "" [[exercises]] name = "testFailure" -path = "exercises/testFailure.rs" mode = "test" hint = "Hello!" diff --git a/tests/fixture/state/info.toml b/tests/fixture/state/info.toml index 8de5d60..e5c4d8f 100644 --- a/tests/fixture/state/info.toml +++ b/tests/fixture/state/info.toml @@ -1,17 +1,14 @@ [[exercises]] name = "pending_exercise" -path = "exercises/pending_exercise.rs" -mode = "compile" +mode = "run" hint = """""" [[exercises]] name = "pending_test_exercise" -path = "exercises/pending_test_exercise.rs" mode = "test" hint = """""" [[exercises]] name = "finished_exercise" -path = "exercises/finished_exercise.rs" -mode = "compile" +mode = "run" hint = """""" diff --git a/tests/fixture/success/info.toml b/tests/fixture/success/info.toml index 17ed8c6..674ba26 100644 --- a/tests/fixture/success/info.toml +++ b/tests/fixture/success/info.toml @@ -1,11 +1,9 @@ [[exercises]] name = "compSuccess" -path = "exercises/compSuccess.rs" -mode = "compile" +mode = "run" hint = """""" [[exercises]] name = "testSuccess" -path = "exercises/testSuccess.rs" mode = "test" hint = """""" -- cgit v1.2.3 From d1ebbaa6f610ec492422806beb34e0dc7e4fc466 Mon Sep 17 00:00:00 2001 From: mo8it Date: Tue, 16 Apr 2024 03:18:22 +0200 Subject: Add format_version to test info.toml files --- tests/fixture/failure/info.toml | 2 ++ tests/fixture/state/info.toml | 2 ++ tests/fixture/success/info.toml | 2 ++ 3 files changed, 6 insertions(+) (limited to 'tests/fixture') diff --git a/tests/fixture/failure/info.toml b/tests/fixture/failure/info.toml index 94ec6ea..ef99a07 100644 --- a/tests/fixture/failure/info.toml +++ b/tests/fixture/failure/info.toml @@ -1,3 +1,5 @@ +format_version = 1 + [[exercises]] name = "compFailure" mode = "run" diff --git a/tests/fixture/state/info.toml b/tests/fixture/state/info.toml index e5c4d8f..eec24ea 100644 --- a/tests/fixture/state/info.toml +++ b/tests/fixture/state/info.toml @@ -1,3 +1,5 @@ +format_version = 1 + [[exercises]] name = "pending_exercise" mode = "run" diff --git a/tests/fixture/success/info.toml b/tests/fixture/success/info.toml index 674ba26..88650ec 100644 --- a/tests/fixture/success/info.toml +++ b/tests/fixture/success/info.toml @@ -1,3 +1,5 @@ +format_version = 1 + [[exercises]] name = "compSuccess" mode = "run" -- cgit v1.2.3 From 2af0cd9ccef07edf27abf7046dbe15e32d1b476d Mon Sep 17 00:00:00 2001 From: mo8it Date: Thu, 25 Apr 2024 03:25:45 +0200 Subject: Replace `mode` by `test` and `strict_clippy` --- info.toml | 143 +++++++++++++--------------------------- src/app_state.rs | 5 +- src/dev/new.rs | 13 ++-- src/exercise.rs | 131 +++++++++++++++++++++--------------- src/info_file.rs | 22 +++---- tests/fixture/failure/info.toml | 3 +- tests/fixture/state/info.toml | 5 +- tests/fixture/success/info.toml | 3 +- 8 files changed, 147 insertions(+), 178 deletions(-) (limited to 'tests/fixture') diff --git a/info.toml b/info.toml index d5369d5..1494472 100644 --- a/info.toml +++ b/info.toml @@ -39,7 +39,7 @@ https://github.com/rust-lang/rustlings/blob/main/CONTRIBUTING.md [[exercises]] name = "intro1" dir = "00_intro" -mode = "run" +test = false # TODO: Fix hint hint = """ Remove the `I AM NOT DONE` comment in the `exercises/intro00/intro1.rs` file @@ -48,7 +48,7 @@ to move on to the next exercise.""" [[exercises]] name = "intro2" dir = "00_intro" -mode = "run" +test = false hint = """ The compiler is informing us that we've got the name of the print macro wrong, and has suggested an alternative.""" @@ -57,7 +57,7 @@ The compiler is informing us that we've got the name of the print macro wrong, a [[exercises]] name = "variables1" dir = "01_variables" -mode = "run" +test = false hint = """ The declaration in the first line in the main function is missing a keyword that is needed in Rust to create a new variable binding.""" @@ -65,7 +65,7 @@ that is needed in Rust to create a new variable binding.""" [[exercises]] name = "variables2" dir = "01_variables" -mode = "run" +test = false hint = """ The compiler message is saying that Rust cannot infer the type that the variable binding `x` has with what is given here. @@ -84,7 +84,7 @@ What if `x` is the same type as `10`? What if it's a different type?""" [[exercises]] name = "variables3" dir = "01_variables" -mode = "run" +test = false hint = """ Oops! In this exercise, we have a variable binding that we've created on in the first line in the `main` function, and we're trying to use it in the next line, @@ -98,7 +98,7 @@ programming language -- thankfully the Rust compiler has caught this for us!""" [[exercises]] name = "variables4" dir = "01_variables" -mode = "run" +test = false hint = """ In Rust, variable bindings are immutable by default. But here we're trying to reassign a different value to `x`! There's a keyword we can use to make @@ -107,7 +107,7 @@ a variable binding mutable instead.""" [[exercises]] name = "variables5" dir = "01_variables" -mode = "run" +test = false hint = """ In `variables4` we already learned how to make an immutable variable mutable using a special keyword. Unfortunately this doesn't help us much in this @@ -125,7 +125,7 @@ Try to solve this exercise afterwards using this technique.""" [[exercises]] name = "variables6" dir = "01_variables" -mode = "run" +test = false hint = """ We know about variables and mutability, but there is another important type of variable available: constants. @@ -145,7 +145,7 @@ https://doc.rust-lang.org/book/ch03-01-variables-and-mutability.html#constants [[exercises]] name = "functions1" dir = "02_functions" -mode = "run" +test = false hint = """ This main function is calling a function that it expects to exist, but the function doesn't exist. It expects this function to have the name `call_me`. @@ -155,7 +155,7 @@ Sounds a lot like `main`, doesn't it?""" [[exercises]] name = "functions2" dir = "02_functions" -mode = "run" +test = false hint = """ Rust requires that all parts of a function's signature have type annotations, but `call_me` is missing the type annotation of `num`.""" @@ -163,7 +163,7 @@ but `call_me` is missing the type annotation of `num`.""" [[exercises]] name = "functions3" dir = "02_functions" -mode = "run" +test = false hint = """ This time, the function *declaration* is okay, but there's something wrong with the place where we're calling the function.""" @@ -171,7 +171,7 @@ with the place where we're calling the function.""" [[exercises]] name = "functions4" dir = "02_functions" -mode = "run" +test = false hint = """ The error message points to the function `sale_price` and says it expects a type after the `->`. This is where the function's return type should be -- take a @@ -180,7 +180,7 @@ look at the `is_even` function for an example!""" [[exercises]] name = "functions5" dir = "02_functions" -mode = "run" +test = false hint = """ This is a really common error that can be fixed by removing one character. It happens because Rust distinguishes between expressions and statements: @@ -199,7 +199,6 @@ They are not the same. There are two solutions: [[exercises]] name = "if1" dir = "03_if" -mode = "test" hint = """ It's possible to do this in one line if you would like! @@ -215,7 +214,6 @@ Remember in Rust that: [[exercises]] name = "if2" dir = "03_if" -mode = "test" hint = """ For that first compiler error, it's important in Rust that each conditional block returns the same type! To get the tests passing, you will need a couple @@ -224,7 +222,6 @@ conditions checking different input values.""" [[exercises]] name = "if3" dir = "03_if" -mode = "test" hint = """ In Rust, every arm of an `if` expression has to return the same type of value. Make sure the type is consistent across all arms.""" @@ -234,7 +231,6 @@ Make sure the type is consistent across all arms.""" [[exercises]] name = "quiz1" dir = "quizzes" -mode = "test" hint = "No hints this time ;)" # PRIMITIVE TYPES @@ -242,19 +238,19 @@ hint = "No hints this time ;)" [[exercises]] name = "primitive_types1" dir = "04_primitive_types" -mode = "run" +test = false hint = "No hints this time ;)" [[exercises]] name = "primitive_types2" dir = "04_primitive_types" -mode = "run" +test = false hint = "No hints this time ;)" [[exercises]] name = "primitive_types3" dir = "04_primitive_types" -mode = "run" +test = false hint = """ There's a shorthand to initialize Arrays with a certain size that does not require you to type in 100 items (but you certainly can if you want!). @@ -270,7 +266,6 @@ for `a.len() >= 100`?""" [[exercises]] name = "primitive_types4" dir = "04_primitive_types" -mode = "test" hint = """ Take a look at the 'Understanding Ownership -> Slices -> Other Slices' section of the book: https://doc.rust-lang.org/book/ch04-03-slices.html and use the @@ -285,7 +280,7 @@ https://doc.rust-lang.org/nomicon/coercions.html""" [[exercises]] name = "primitive_types5" dir = "04_primitive_types" -mode = "run" +test = false hint = """ Take a look at the 'Data Types -> The Tuple Type' section of the book: https://doc.rust-lang.org/book/ch03-02-data-types.html#the-tuple-type @@ -298,7 +293,6 @@ of the tuple. You can do it!!""" [[exercises]] name = "primitive_types6" dir = "04_primitive_types" -mode = "test" hint = """ While you could use a destructuring `let` for the tuple here, try indexing into it instead, as explained in the last example of the @@ -311,7 +305,6 @@ Now you have another tool in your toolbox!""" [[exercises]] name = "vecs1" dir = "05_vecs" -mode = "test" hint = """ In Rust, there are two ways to define a Vector. 1. One way is to use the `Vec::new()` function to create a new vector @@ -326,7 +319,6 @@ of the Rust book to learn more. [[exercises]] name = "vecs2" dir = "05_vecs" -mode = "test" hint = """ In the first function we are looping over the Vector and getting a reference to one `element` at a time. @@ -349,7 +341,6 @@ What do you think is the more commonly used pattern under Rust developers? [[exercises]] name = "move_semantics1" dir = "06_move_semantics" -mode = "test" hint = """ So you've got the "cannot borrow immutable local variable `vec` as mutable" error on the line where we push an element to the vector, right? @@ -363,7 +354,6 @@ happens!""" [[exercises]] name = "move_semantics2" dir = "06_move_semantics" -mode = "test" hint = """ When running this exercise for the first time, you'll notice an error about "borrow of moved value". In Rust, when an argument is passed to a function and @@ -384,7 +374,6 @@ try them all: [[exercises]] name = "move_semantics3" dir = "06_move_semantics" -mode = "test" hint = """ The difference between this one and the previous ones is that the first line of `fn fill_vec` that had `let mut vec = vec;` is no longer there. You can, @@ -394,7 +383,6 @@ an existing binding to be a mutable binding instead of an immutable one :)""" [[exercises]] name = "move_semantics4" dir = "06_move_semantics" -mode = "test" hint = """ Stop reading whenever you feel like you have enough direction :) Or try doing one step and then fixing the compiler errors that result! @@ -408,7 +396,6 @@ So the end goal is to: [[exercises]] name = "move_semantics5" dir = "06_move_semantics" -mode = "test" hint = """ Carefully reason about the range in which each mutable reference is in scope. Does it help to update the value of referent (`x`) immediately after @@ -420,7 +407,7 @@ https://doc.rust-lang.org/book/ch04-02-references-and-borrowing.html#mutable-ref [[exercises]] name = "move_semantics6" dir = "06_move_semantics" -mode = "run" +test = false hint = """ To find the answer, you can consult the book section "References and Borrowing": https://doc.rust-lang.org/stable/book/ch04-02-references-and-borrowing.html @@ -441,7 +428,6 @@ Another hint: it has to do with the `&` character.""" [[exercises]] name = "structs1" dir = "07_structs" -mode = "test" hint = """ Rust has more than one type of struct. Three actually, all variants are used to package related data together. @@ -461,7 +447,6 @@ https://doc.rust-lang.org/book/ch05-01-defining-structs.html""" [[exercises]] name = "structs2" dir = "07_structs" -mode = "test" hint = """ Creating instances of structs is easy, all you need to do is assign some values to its fields. @@ -473,7 +458,6 @@ https://doc.rust-lang.org/stable/book/ch05-01-defining-structs.html#creating-ins [[exercises]] name = "structs3" dir = "07_structs" -mode = "test" hint = """ For `is_international`: What makes a package international? Seems related to the places it goes through right? @@ -489,13 +473,13 @@ https://doc.rust-lang.org/book/ch05-03-method-syntax.html""" [[exercises]] name = "enums1" dir = "08_enums" -mode = "run" +test = false hint = "No hints this time ;)" [[exercises]] name = "enums2" dir = "08_enums" -mode = "run" +test = false hint = """ You can create enumerations that have different variants with different types such as no data, anonymous structs, a single string, tuples, ...etc""" @@ -503,7 +487,6 @@ such as no data, anonymous structs, a single string, tuples, ...etc""" [[exercises]] name = "enums3" dir = "08_enums" -mode = "test" hint = """ As a first step, you can define enums to compile this code without errors. @@ -517,7 +500,7 @@ to get value in the variant.""" [[exercises]] name = "strings1" dir = "09_strings" -mode = "run" +test = false hint = """ The `current_favorite_color` function is currently returning a string slice with the `'static` lifetime. We know this because the data of the string lives @@ -531,7 +514,7 @@ another way that uses the `From` trait.""" [[exercises]] name = "strings2" dir = "09_strings" -mode = "run" +test = false hint = """ Yes, it would be really easy to fix this by just changing the value bound to `word` to be a string slice instead of a `String`, wouldn't it?? There is a way @@ -546,7 +529,6 @@ https://doc.rust-lang.org/stable/book/ch15-02-deref.html#implicit-deref-coercion [[exercises]] name = "strings3" dir = "09_strings" -mode = "test" hint = """ There's tons of useful standard library functions for strings. Let's try and use some of them: https://doc.rust-lang.org/std/string/struct.String.html#method.trim @@ -557,7 +539,7 @@ the string slice into an owned string, which you can then freely extend.""" [[exercises]] name = "strings4" dir = "09_strings" -mode = "run" +test = false hint = "No hints this time ;)" # MODULES @@ -565,7 +547,7 @@ hint = "No hints this time ;)" [[exercises]] name = "modules1" dir = "10_modules" -mode = "run" +test = false hint = """ Everything is private in Rust by default-- but there's a keyword we can use to make something public! The compiler error should point to the thing that @@ -574,7 +556,7 @@ needs to be public.""" [[exercises]] name = "modules2" dir = "10_modules" -mode = "run" +test = false hint = """ The delicious_snacks module is trying to present an external interface that is different than its internal structure (the `fruits` and `veggies` modules and @@ -586,7 +568,7 @@ Learn more at https://doc.rust-lang.org/book/ch07-04-bringing-paths-into-scope-w [[exercises]] name = "modules3" dir = "10_modules" -mode = "run" +test = false hint = """ `UNIX_EPOCH` and `SystemTime` are declared in the `std::time` module. Add a `use` statement for these two to bring them into scope. You can use nested @@ -597,7 +579,6 @@ paths or the glob operator to bring these two in using only one line.""" [[exercises]] name = "hashmaps1" dir = "11_hashmaps" -mode = "test" hint = """ Hint 1: Take a look at the return type of the function to figure out the type for the `basket`. @@ -609,7 +590,6 @@ Hint 2: Number of fruits should be at least 5. And you have to put [[exercises]] name = "hashmaps2" dir = "11_hashmaps" -mode = "test" hint = """ Use the `entry()` and `or_insert()` methods of `HashMap` to achieve this. Learn more at https://doc.rust-lang.org/stable/book/ch08-03-hash-maps.html#only-inserting-a-value-if-the-key-has-no-value @@ -618,7 +598,6 @@ Learn more at https://doc.rust-lang.org/stable/book/ch08-03-hash-maps.html#only- [[exercises]] name = "hashmaps3" dir = "11_hashmaps" -mode = "test" hint = """ Hint 1: Use the `entry()` and `or_insert()` methods of `HashMap` to insert entries corresponding to each team in the scores table. @@ -636,7 +615,6 @@ Learn more at https://doc.rust-lang.org/book/ch08-03-hash-maps.html#updating-a-v [[exercises]] name = "quiz2" dir = "quizzes" -mode = "test" hint = "No hints this time ;)" # OPTIONS @@ -644,7 +622,6 @@ hint = "No hints this time ;)" [[exercises]] name = "options1" dir = "12_options" -mode = "test" hint = """ Options can have a `Some` value, with an inner value, or a `None` value, without an inner value. @@ -656,7 +633,6 @@ it doesn't panic in your face later?""" [[exercises]] name = "options2" dir = "12_options" -mode = "test" hint = """ Check out: @@ -673,7 +649,7 @@ Also see `Option::flatten` [[exercises]] name = "options3" dir = "12_options" -mode = "run" +test = false hint = """ The compiler says a partial move happened in the `match` statement. How can this be avoided? The compiler shows the correction needed. @@ -686,7 +662,6 @@ https://doc.rust-lang.org/std/keyword.ref.html""" [[exercises]] name = "errors1" dir = "13_error_handling" -mode = "test" hint = """ `Ok` and `Err` are the two variants of `Result`, so what the tests are saying is that `generate_nametag_text` should return a `Result` instead of an `Option`. @@ -702,7 +677,6 @@ To make this change, you'll need to: [[exercises]] name = "errors2" dir = "13_error_handling" -mode = "test" hint = """ One way to handle this is using a `match` statement on `item_quantity.parse::()` where the cases are `Ok(something)` and @@ -718,7 +692,7 @@ and give it a try!""" [[exercises]] name = "errors3" dir = "13_error_handling" -mode = "run" +test = false hint = """ If other functions can return a `Result`, why shouldn't `main`? It's a fairly common convention to return something like `Result<(), ErrorType>` from your @@ -730,7 +704,6 @@ positive results.""" [[exercises]] name = "errors4" dir = "13_error_handling" -mode = "test" hint = """ `PositiveNonzeroInteger::new` is always creating a new instance and returning an `Ok` result. @@ -742,7 +715,7 @@ everything is... okay :)""" [[exercises]] name = "errors5" dir = "13_error_handling" -mode = "run" +test = false hint = """ There are two different possible `Result` types produced within `main()`, which are propagated using `?` operators. How do we declare a return type from @@ -766,7 +739,6 @@ https://doc.rust-lang.org/stable/rust-by-example/error/multiple_error_types/reen [[exercises]] name = "errors6" dir = "13_error_handling" -mode = "test" hint = """ This exercise uses a completed version of `PositiveNonzeroInteger` from errors4. @@ -788,7 +760,7 @@ https://doc.rust-lang.org/std/result/enum.Result.html#method.map_err""" [[exercises]] name = "generics1" dir = "14_generics" -mode = "run" +test = false hint = """ Vectors in Rust make use of generics to create dynamically sized arrays of any type. @@ -798,7 +770,6 @@ You need to tell the compiler what type we are pushing onto this vector.""" [[exercises]] name = "generics2" dir = "14_generics" -mode = "test" hint = """ Currently we are wrapping only values of type `u32`. @@ -812,7 +783,6 @@ If you are still stuck https://doc.rust-lang.org/stable/book/ch10-01-syntax.html [[exercises]] name = "traits1" dir = "15_traits" -mode = "test" hint = """ A discussion about Traits in Rust can be found at: https://doc.rust-lang.org/book/ch10-02-traits.html @@ -821,7 +791,6 @@ https://doc.rust-lang.org/book/ch10-02-traits.html [[exercises]] name = "traits2" dir = "15_traits" -mode = "test" hint = """ Notice how the trait takes ownership of `self`, and returns `Self`. @@ -834,7 +803,6 @@ the documentation at: https://doc.rust-lang.org/std/vec/struct.Vec.html""" [[exercises]] name = "traits3" dir = "15_traits" -mode = "test" hint = """ Traits can have a default implementation for functions. Structs that implement the trait can then use the default version of these functions if they choose not @@ -846,7 +814,6 @@ See the documentation at: https://doc.rust-lang.org/book/ch10-02-traits.html#def [[exercises]] name = "traits4" dir = "15_traits" -mode = "test" hint = """ Instead of using concrete types as parameters you can use traits. Try replacing the '??' with 'impl ' @@ -857,7 +824,7 @@ See the documentation at: https://doc.rust-lang.org/book/ch10-02-traits.html#tra [[exercises]] name = "traits5" dir = "15_traits" -mode = "run" +test = false hint = """ To ensure a parameter implements multiple traits use the '+ syntax'. Try replacing the '??' with 'impl <> + <>'. @@ -870,7 +837,6 @@ See the documentation at: https://doc.rust-lang.org/book/ch10-02-traits.html#spe [[exercises]] name = "quiz3" dir = "quizzes" -mode = "test" hint = """ To find the best solution to this challenge you're going to need to think back to your knowledge of traits, specifically 'Trait Bound Syntax' @@ -882,7 +848,7 @@ You may also need this: `use std::fmt::Display;`.""" [[exercises]] name = "lifetimes1" dir = "16_lifetimes" -mode = "run" +test = false hint = """ Let the compiler guide you. Also take a look at the book if you need help: https://doc.rust-lang.org/book/ch10-03-lifetime-syntax.html""" @@ -890,7 +856,7 @@ https://doc.rust-lang.org/book/ch10-03-lifetime-syntax.html""" [[exercises]] name = "lifetimes2" dir = "16_lifetimes" -mode = "run" +test = false hint = """ Remember that the generic lifetime `'a` will get the concrete lifetime that is equal to the smaller of the lifetimes of `x` and `y`. @@ -904,7 +870,7 @@ inner block: [[exercises]] name = "lifetimes3" dir = "16_lifetimes" -mode = "run" +test = false hint = """ If you use a lifetime annotation in a struct's fields, where else does it need to be added?""" @@ -914,7 +880,6 @@ to be added?""" [[exercises]] name = "tests1" dir = "17_tests" -mode = "test" hint = """ You don't even need to write any code to test -- you can just test values and run that, even though you wouldn't do that in real life. :) @@ -929,7 +894,6 @@ ones pass, and which ones fail :)""" [[exercises]] name = "tests2" dir = "17_tests" -mode = "test" hint = """ Like the previous exercise, you don't need to write any code to get this test to compile and run. @@ -942,7 +906,6 @@ argument comes first and which comes second!""" [[exercises]] name = "tests3" dir = "17_tests" -mode = "test" hint = """ You can call a function right where you're passing arguments to `assert!`. So you could do something like `assert!(having_fun())`. @@ -953,7 +916,6 @@ what you're doing using `!`, like `assert!(!having_fun())`.""" [[exercises]] name = "tests4" dir = "17_tests" -mode = "test" hint = """ We expect method `Rectangle::new()` to panic for negative values. @@ -967,7 +929,6 @@ https://doc.rust-lang.org/stable/book/ch11-01-writing-tests.html#checking-for-pa [[exercises]] name = "iterators1" dir = "18_iterators" -mode = "test" hint = """ Step 1: @@ -990,7 +951,6 @@ https://doc.rust-lang.org/std/iter/trait.Iterator.html for some ideas. [[exercises]] name = "iterators2" dir = "18_iterators" -mode = "test" hint = """ Step 1: @@ -1016,7 +976,6 @@ powerful and very general. Rust just needs to know the desired type.""" [[exercises]] name = "iterators3" dir = "18_iterators" -mode = "test" hint = """ The `divide` function needs to return the correct error when even division is not possible. @@ -1035,7 +994,6 @@ powerful! It can make the solution to this exercise infinitely easier.""" [[exercises]] name = "iterators4" dir = "18_iterators" -mode = "test" hint = """ In an imperative language, you might write a `for` loop that updates a mutable variable. Or, you might write code utilizing recursion and a match clause. In @@ -1047,7 +1005,6 @@ Hint 2: Check out the `fold` and `rfold` methods!""" [[exercises]] name = "iterators5" dir = "18_iterators" -mode = "test" hint = """ The documentation for the `std::iter::Iterator` trait contains numerous methods that would be helpful here. @@ -1066,7 +1023,6 @@ a different method that could make your code more compact than using `fold`.""" [[exercises]] name = "box1" dir = "19_smart_pointers" -mode = "test" hint = """ Step 1: @@ -1090,7 +1046,6 @@ definition and try other types! [[exercises]] name = "rc1" dir = "19_smart_pointers" -mode = "test" hint = """ This is a straightforward exercise to use the `Rc` type. Each `Planet` has ownership of the `Sun`, and uses `Rc::clone()` to increment the reference count @@ -1109,7 +1064,7 @@ See more at: https://doc.rust-lang.org/book/ch15-04-rc.html [[exercises]] name = "arc1" dir = "19_smart_pointers" -mode = "run" +test = false hint = """ Make `shared_numbers` be an `Arc` from the numbers vector. Then, in order to avoid creating a copy of `numbers`, you'll need to create `child_numbers` @@ -1127,7 +1082,6 @@ https://doc.rust-lang.org/stable/book/ch16-00-concurrency.html [[exercises]] name = "cow1" dir = "19_smart_pointers" -mode = "test" hint = """ If `Cow` already owns the data it doesn't need to clone it when `to_mut()` is called. @@ -1141,7 +1095,7 @@ on the `Cow` type. [[exercises]] name = "threads1" dir = "20_threads" -mode = "run" +test = false hint = """ `JoinHandle` is a struct that is returned from a spawned thread: https://doc.rust-lang.org/std/thread/fn.spawn.html @@ -1159,7 +1113,7 @@ https://doc.rust-lang.org/std/thread/struct.JoinHandle.html [[exercises]] name = "threads2" dir = "20_threads" -mode = "run" +test = false hint = """ `Arc` is an Atomic Reference Counted pointer that allows safe, shared access to **immutable** data. But we want to *change* the number of `jobs_completed` @@ -1181,7 +1135,6 @@ https://doc.rust-lang.org/book/ch16-03-shared-state.html#sharing-a-mutext-betwee [[exercises]] name = "threads3" dir = "20_threads" -mode = "test" hint = """ An alternate way to handle concurrency between threads is to use an `mpsc` (multiple producer, single consumer) channel to communicate. @@ -1200,7 +1153,7 @@ See https://doc.rust-lang.org/book/ch16-02-message-passing.html for more info. [[exercises]] name = "macros1" dir = "21_macros" -mode = "run" +test = false hint = """ When you call a macro, you need to add something special compared to a regular function call. If you're stuck, take a look at what's inside @@ -1209,7 +1162,7 @@ regular function call. If you're stuck, take a look at what's inside [[exercises]] name = "macros2" dir = "21_macros" -mode = "run" +test = false hint = """ Macros don't quite play by the same rules as the rest of Rust, in terms of what's available where. @@ -1220,7 +1173,7 @@ Unlike other things in Rust, the order of "where you define a macro" versus [[exercises]] name = "macros3" dir = "21_macros" -mode = "run" +test = false hint = """ In order to use a macro outside of its module, you need to do something special to the module to lift the macro out into its parent. @@ -1231,7 +1184,7 @@ exported macros, if you've seen any of those around.""" [[exercises]] name = "macros4" dir = "21_macros" -mode = "run" +test = false hint = """ You only need to add a single character to make this compile. @@ -1248,7 +1201,8 @@ https://veykril.github.io/tlborm/""" [[exercises]] name = "clippy1" dir = "22_clippy" -mode = "clippy" +test = false +strict_clippy = true hint = """ Rust stores the highest precision version of any long or infinite precision mathematical constants in the Rust standard library: @@ -1264,14 +1218,16 @@ appropriate replacement constant from `std::f32::consts`...""" [[exercises]] name = "clippy2" dir = "22_clippy" -mode = "clippy" +test = false +strict_clippy = true hint = """ `for` loops over `Option` values are more clearly expressed as an `if let`""" [[exercises]] name = "clippy3" dir = "22_clippy" -mode = "clippy" +test = false +strict_clippy = true hint = "No hints this time!" # TYPE CONVERSIONS @@ -1279,7 +1235,6 @@ hint = "No hints this time!" [[exercises]] name = "using_as" dir = "23_conversions" -mode = "test" hint = """ Use the `as` operator to cast one of the operands in the last line of the `average` function into the expected return type.""" @@ -1287,14 +1242,12 @@ Use the `as` operator to cast one of the operands in the last line of the [[exercises]] name = "from_into" dir = "23_conversions" -mode = "test" hint = """ Follow the steps provided right before the `From` implementation""" [[exercises]] name = "from_str" dir = "23_conversions" -mode = "test" hint = """ The implementation of `FromStr` should return an `Ok` with a `Person` object, or an `Err` with an error if the string is not valid. @@ -1315,7 +1268,6 @@ https://doc.rust-lang.org/stable/rust-by-example/error/multiple_error_types/reen [[exercises]] name = "try_from_into" dir = "23_conversions" -mode = "test" hint = """ Follow the steps provided right before the `TryFrom` implementation. You can also use the example at @@ -1338,6 +1290,5 @@ Challenge: Can you make the `TryFrom` implementations generic over many integer [[exercises]] name = "as_ref_mut" dir = "23_conversions" -mode = "test" hint = """ Add `AsRef` or `AsMut` as a trait bound to the functions.""" diff --git a/src/app_state.rs b/src/app_state.rs index 11ac8ee..476b5a9 100644 --- a/src/app_state.rs +++ b/src/app_state.rs @@ -112,7 +112,8 @@ impl AppState { dir, name, path, - mode: exercise_info.mode, + test: exercise_info.test, + strict_clippy: exercise_info.strict_clippy, hint, done: false, } @@ -329,8 +330,6 @@ impl AppState { } writeln!(writer, "{}", "ok".green())?; - - output.clear(); } writer.execute(Clear(ClearType::All))?; diff --git a/src/dev/new.rs b/src/dev/new.rs index 82aba42..8f87010 100644 --- a/src/dev/new.rs +++ b/src/dev/new.rs @@ -99,10 +99,15 @@ name = "???" # Otherwise, the path is `exercises/NAME.rs` # dir = "???" -# The mode to run the exercise in. -# The mode "test" (preferred) runs the exercise's tests. -# The mode "run" only checks if the exercise compiles and runs it. -mode = "test" +# Rustlings expects the exercise to contain tests and run them. +# You can optionally disable testing by setting `test` to `false` (the default is `true`). +# In that case, the exercise will be considered done when it just successfully compiles. +# test = true + +# Rustlings will always run Clippy on exercises. +# You can optionally set `strict_clippy` to `true` (the default is `false`) to only consider +# the exercise as done when there are no warnings left. +# strict_clippy = false # A multi-line hint to be shown to users on request. hint = """???""" diff --git a/src/exercise.rs b/src/exercise.rs index 17cc8d7..366dc2b 100644 --- a/src/exercise.rs +++ b/src/exercise.rs @@ -3,24 +3,38 @@ use crossterm::style::{style, StyledContent, Stylize}; use std::{ fmt::{self, Display, Formatter}, io::{Read, Write}, - process::Command, + process::{Command, Stdio}, }; -use crate::{in_official_repo, info_file::Mode, terminal_link::TerminalFileLink, DEBUG_PROFILE}; +use crate::{in_official_repo, terminal_link::TerminalFileLink, DEBUG_PROFILE}; // TODO pub const OUTPUT_CAPACITY: usize = 1 << 12; -fn run_command(mut cmd: Command, cmd_description: &str, output: &mut Vec) -> Result { +fn run_command( + mut cmd: Command, + cmd_description: &str, + output: &mut Vec, + stderr: bool, +) -> Result { let (mut reader, writer) = os_pipe::pipe().with_context(|| { format!("Failed to create a pipe to run the command `{cmd_description}``") })?; + let (stdout, stderr) = if stderr { + ( + Stdio::from(writer.try_clone().with_context(|| { + format!("Failed to clone the pipe writer for the command `{cmd_description}`") + })?), + Stdio::from(writer), + ) + } else { + (Stdio::from(writer), Stdio::null()) + }; + let mut handle = cmd - .stdout(writer.try_clone().with_context(|| { - format!("Failed to clone the pipe writer for the command `{cmd_description}`") - })?) - .stderr(writer) + .stdout(stdout) + .stderr(stderr) .spawn() .with_context(|| format!("Failed to run the command `{cmd_description}`"))?; @@ -45,8 +59,8 @@ pub struct Exercise { pub name: &'static str, // Exercise's path pub path: &'static str, - // The mode of the exercise - pub mode: Mode, + pub test: bool, + pub strict_clippy: bool, // The hint text associated with the exercise pub hint: String, pub done: bool, @@ -54,10 +68,22 @@ pub struct Exercise { impl Exercise { fn run_bin(&self, output: &mut Vec) -> Result { - writeln!(output, "{}", "Output".bold().magenta().underlined())?; + writeln!(output, "{}", "Output".underlined())?; let bin_path = format!("target/debug/{}", self.name); - run_command(Command::new(&bin_path), &bin_path, output) + let success = run_command(Command::new(&bin_path), &bin_path, output, true)?; + + if !success { + writeln!( + output, + "{}", + "The exercise didn't run successfully (nonzero exit code)" + .bold() + .red() + )?; + } + + Ok(success) } fn cargo_cmd( @@ -67,6 +93,7 @@ impl Exercise { cmd_description: &str, output: &mut Vec, dev: bool, + stderr: bool, ) -> Result { let mut cmd = Command::new("cargo"); cmd.arg(command); @@ -86,25 +113,7 @@ impl Exercise { .arg(self.name) .args(args); - run_command(cmd, cmd_description, output) - } - - fn cargo_cmd_with_bin_output( - &self, - command: &str, - args: &[&str], - cmd_description: &str, - output: &mut Vec, - dev: bool, - ) -> Result { - // Discard the output of `cargo build` because it will be shown again by the Cargo command. - output.clear(); - - let cargo_cmd_success = self.cargo_cmd(command, args, cmd_description, output, dev)?; - - let run_success = self.run_bin(output)?; - - Ok(cargo_cmd_success && run_success) + run_command(cmd, cmd_description, output, stderr) } pub fn run(&self, output: &mut Vec) -> Result { @@ -113,35 +122,49 @@ impl Exercise { // Developing the official Rustlings. let dev = DEBUG_PROFILE && in_official_repo(); - let build_success = self.cargo_cmd("build", &[], "cargo build …", output, dev)?; + let build_success = self.cargo_cmd("build", &[], "cargo build …", output, dev, true)?; if !build_success { return Ok(false); } - match self.mode { - Mode::Run => self.run_bin(output), - Mode::Test => self.cargo_cmd_with_bin_output( - "test", - &[ - "--", - "--color", - "always", - "--nocapture", - "--format", - "pretty", - ], - "cargo test …", - output, - dev, - ), - Mode::Clippy => self.cargo_cmd_with_bin_output( - "clippy", - &["--", "-D", "warnings"], - "cargo clippy …", - output, - dev, - ), + // Discard the output of `cargo build` because it will be shown again by the Cargo command. + output.clear(); + + let clippy_args: &[&str] = if self.strict_clippy { + &["--", "-D", "warnings"] + } else { + &[] + }; + let clippy_success = + self.cargo_cmd("clippy", clippy_args, "cargo clippy …", output, dev, true)?; + if !clippy_success { + return Ok(false); + } + + if !self.test { + return self.run_bin(output); } + + let test_success = self.cargo_cmd( + "test", + &[ + "--", + "--color", + "always", + "--nocapture", + "--format", + "pretty", + ], + "cargo test …", + output, + dev, + // Hide warnings because they are shown by Clippy. + false, + )?; + + let run_success = self.run_bin(output)?; + + Ok(test_success && run_success) } pub fn terminal_link(&self) -> StyledContent> { diff --git a/src/info_file.rs b/src/info_file.rs index 6938cd0..dbe4f08 100644 --- a/src/info_file.rs +++ b/src/info_file.rs @@ -2,18 +2,6 @@ use anyhow::{bail, Context, Error, Result}; use serde::Deserialize; use std::{fs, io::ErrorKind}; -// The mode of the exercise. -#[derive(Deserialize, Copy, Clone)] -#[serde(rename_all = "lowercase")] -pub enum Mode { - // The exercise should be compiled as a binary - Run, - // The exercise should be compiled as a test harness - Test, - // The exercise should be linted with clippy - Clippy, -} - // Deserialized from the `info.toml` file. #[derive(Deserialize)] pub struct ExerciseInfo { @@ -21,11 +9,17 @@ pub struct ExerciseInfo { pub name: String, // The exercise's directory inside the `exercises` directory pub dir: Option, - // The mode of the exercise - pub mode: Mode, + #[serde(default = "default_true")] + pub test: bool, + #[serde(default)] + pub strict_clippy: bool, // The hint text associated with the exercise pub hint: String, } +#[inline] +const fn default_true() -> bool { + true +} impl ExerciseInfo { pub fn path(&self) -> String { diff --git a/tests/fixture/failure/info.toml b/tests/fixture/failure/info.toml index ef99a07..554607a 100644 --- a/tests/fixture/failure/info.toml +++ b/tests/fixture/failure/info.toml @@ -2,10 +2,9 @@ format_version = 1 [[exercises]] name = "compFailure" -mode = "run" +test = false hint = "" [[exercises]] name = "testFailure" -mode = "test" hint = "Hello!" diff --git a/tests/fixture/state/info.toml b/tests/fixture/state/info.toml index eec24ea..ff0b932 100644 --- a/tests/fixture/state/info.toml +++ b/tests/fixture/state/info.toml @@ -2,15 +2,14 @@ format_version = 1 [[exercises]] name = "pending_exercise" -mode = "run" +test = false hint = """""" [[exercises]] name = "pending_test_exercise" -mode = "test" hint = """""" [[exercises]] name = "finished_exercise" -mode = "run" +test = false hint = """""" diff --git a/tests/fixture/success/info.toml b/tests/fixture/success/info.toml index 88650ec..d66d7d4 100644 --- a/tests/fixture/success/info.toml +++ b/tests/fixture/success/info.toml @@ -2,10 +2,9 @@ format_version = 1 [[exercises]] name = "compSuccess" -mode = "run" +test = false hint = """""" [[exercises]] name = "testSuccess" -mode = "test" hint = """""" -- cgit v1.2.3 From d26f47ddddaabcde36d5cb02ec220ccd1c141f10 Mon Sep 17 00:00:00 2001 From: mo8it Date: Thu, 25 Apr 2024 03:27:41 +0200 Subject: Fix tests --- tests/fixture/failure/exercises/compNoExercise.rs | 3 +-- tests/fixture/failure/exercises/testFailure.rs | 2 ++ tests/fixture/failure/exercises/testNotPassed.rs | 2 ++ tests/fixture/state/exercises/finished_exercise.rs | 6 +----- tests/fixture/state/exercises/pending_exercise.rs | 6 +----- tests/fixture/state/exercises/pending_test_exercise.rs | 2 ++ tests/fixture/success/exercises/testSuccess.rs | 2 ++ 7 files changed, 11 insertions(+), 12 deletions(-) (limited to 'tests/fixture') diff --git a/tests/fixture/failure/exercises/compNoExercise.rs b/tests/fixture/failure/exercises/compNoExercise.rs index f79c691..f328e4d 100644 --- a/tests/fixture/failure/exercises/compNoExercise.rs +++ b/tests/fixture/failure/exercises/compNoExercise.rs @@ -1,2 +1 @@ -fn main() { -} +fn main() {} diff --git a/tests/fixture/failure/exercises/testFailure.rs b/tests/fixture/failure/exercises/testFailure.rs index b33a5d2..fcbcf90 100644 --- a/tests/fixture/failure/exercises/testFailure.rs +++ b/tests/fixture/failure/exercises/testFailure.rs @@ -1,3 +1,5 @@ +fn main() {} + #[test] fn passing() { asset!(true); diff --git a/tests/fixture/failure/exercises/testNotPassed.rs b/tests/fixture/failure/exercises/testNotPassed.rs index a9fe88d..de0d61c 100644 --- a/tests/fixture/failure/exercises/testNotPassed.rs +++ b/tests/fixture/failure/exercises/testNotPassed.rs @@ -1,3 +1,5 @@ +fn main() {} + #[test] fn not_passing() { assert!(false); diff --git a/tests/fixture/state/exercises/finished_exercise.rs b/tests/fixture/state/exercises/finished_exercise.rs index 016b827..f328e4d 100644 --- a/tests/fixture/state/exercises/finished_exercise.rs +++ b/tests/fixture/state/exercises/finished_exercise.rs @@ -1,5 +1 @@ -// fake_exercise - -fn main() { - -} +fn main() {} diff --git a/tests/fixture/state/exercises/pending_exercise.rs b/tests/fixture/state/exercises/pending_exercise.rs index 016b827..f328e4d 100644 --- a/tests/fixture/state/exercises/pending_exercise.rs +++ b/tests/fixture/state/exercises/pending_exercise.rs @@ -1,5 +1 @@ -// fake_exercise - -fn main() { - -} +fn main() {} diff --git a/tests/fixture/state/exercises/pending_test_exercise.rs b/tests/fixture/state/exercises/pending_test_exercise.rs index 2002ef1..718e1db 100644 --- a/tests/fixture/state/exercises/pending_test_exercise.rs +++ b/tests/fixture/state/exercises/pending_test_exercise.rs @@ -1,2 +1,4 @@ +fn main() {} + #[test] fn it_works() {} diff --git a/tests/fixture/success/exercises/testSuccess.rs b/tests/fixture/success/exercises/testSuccess.rs index 7139b50..4296cf6 100644 --- a/tests/fixture/success/exercises/testSuccess.rs +++ b/tests/fixture/success/exercises/testSuccess.rs @@ -1,3 +1,5 @@ +fn main() {} + #[test] fn passing() { println!("THIS TEST TOO SHALL PASS"); -- cgit v1.2.3 From c1d28b502eb88aa06acf196ff055f285a8482132 Mon Sep 17 00:00:00 2001 From: mo8it Date: Thu, 25 Apr 2024 14:51:14 +0200 Subject: Format test file :P --- tests/fixture/success/exercises/compSuccess.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'tests/fixture') diff --git a/tests/fixture/success/exercises/compSuccess.rs b/tests/fixture/success/exercises/compSuccess.rs index f79c691..f328e4d 100644 --- a/tests/fixture/success/exercises/compSuccess.rs +++ b/tests/fixture/success/exercises/compSuccess.rs @@ -1,2 +1 @@ -fn main() { -} +fn main() {} -- cgit v1.2.3