summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.editorconfig7
-rw-r--r--Cargo.lock8
-rw-r--r--Cargo.toml2
-rw-r--r--exercises/enums/enums1.rs2
-rw-r--r--exercises/enums/enums2.rs2
-rw-r--r--exercises/enums/enums3.rs2
-rw-r--r--exercises/error_handling/errors1.rs2
-rw-r--r--exercises/error_handling/errors2.rs2
-rw-r--r--exercises/error_handling/errors3.rs2
-rw-r--r--exercises/error_handling/errorsn.rs2
-rw-r--r--exercises/error_handling/option1.rs2
-rw-r--r--exercises/error_handling/result1.rs2
-rw-r--r--exercises/functions/functions1.rs2
-rw-r--r--exercises/functions/functions2.rs2
-rw-r--r--exercises/functions/functions3.rs2
-rw-r--r--exercises/functions/functions4.rs2
-rw-r--r--exercises/functions/functions5.rs2
-rw-r--r--exercises/if/if1.rs2
-rw-r--r--exercises/macros/macros1.rs2
-rw-r--r--exercises/macros/macros2.rs2
-rw-r--r--exercises/macros/macros3.rs2
-rw-r--r--exercises/macros/macros4.rs2
-rw-r--r--exercises/modules/modules1.rs2
-rw-r--r--exercises/modules/modules2.rs2
-rw-r--r--exercises/move_semantics/move_semantics1.rs2
-rw-r--r--exercises/move_semantics/move_semantics2.rs2
-rw-r--r--exercises/move_semantics/move_semantics3.rs2
-rw-r--r--exercises/move_semantics/move_semantics4.rs2
-rw-r--r--exercises/primitive_types/primitive_types1.rs2
-rw-r--r--exercises/primitive_types/primitive_types2.rs2
-rw-r--r--exercises/primitive_types/primitive_types3.rs2
-rw-r--r--exercises/primitive_types/primitive_types4.rs2
-rw-r--r--exercises/primitive_types/primitive_types5.rs2
-rw-r--r--exercises/primitive_types/primitive_types6.rs2
-rw-r--r--exercises/standard_library_types/arc1.rs2
-rw-r--r--exercises/standard_library_types/iterators2.rs2
-rw-r--r--exercises/standard_library_types/iterators3.rs2
-rw-r--r--exercises/standard_library_types/iterators4.rs2
-rw-r--r--exercises/strings/strings1.rs2
-rw-r--r--exercises/strings/strings2.rs2
-rw-r--r--exercises/structs/structs1.rs2
-rw-r--r--exercises/structs/structs2.rs2
-rw-r--r--exercises/test1.rs2
-rw-r--r--exercises/test2.rs2
-rw-r--r--exercises/test3.rs2
-rw-r--r--exercises/test4.rs2
-rw-r--r--exercises/tests/tests1.rs2
-rw-r--r--exercises/tests/tests2.rs2
-rw-r--r--exercises/tests/tests3.rs2
-rw-r--r--exercises/threads/threads1.rs2
-rw-r--r--exercises/variables/variables1.rs7
-rw-r--r--exercises/variables/variables2.rs2
-rw-r--r--exercises/variables/variables3.rs2
-rw-r--r--exercises/variables/variables4.rs2
-rw-r--r--src/exercise.rs113
-rw-r--r--src/main.rs4
-rw-r--r--src/run.rs4
-rw-r--r--src/verify.rs56
-rw-r--r--tests/fixture/state/finished_exercise.rs0
-rw-r--r--tests/fixture/state/pending_exercise.rs7
-rw-r--r--tests/integration_tests.rs19
61 files changed, 315 insertions, 12 deletions
diff --git a/.editorconfig b/.editorconfig
new file mode 100644
index 0000000..89cf181
--- /dev/null
+++ b/.editorconfig
@@ -0,0 +1,7 @@
+root = true
+
+[*.rs]
+end_of_line = lf
+insert_final_newfile = true
+indent_style = space
+indent_size = 4
diff --git a/Cargo.lock b/Cargo.lock
index c88baa6..19572d5 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -205,6 +205,11 @@ version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
+name = "glob"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
name = "indicatif"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -605,8 +610,10 @@ dependencies = [
"assert_cmd 0.11.1 (registry+https://github.com/rust-lang/crates.io-index)",
"clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)",
"console 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "glob 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
"indicatif 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
"notify 4.0.12 (registry+https://github.com/rust-lang/crates.io-index)",
+ "regex 1.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.92 (registry+https://github.com/rust-lang/crates.io-index)",
"toml 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)",
]
@@ -848,6 +855,7 @@ dependencies = [
"checksum fuchsia-cprng 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba"
"checksum fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82"
"checksum fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7"
+"checksum glob 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574"
"checksum indicatif 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a29b2fa6f00010c268bface64c18bb0310aaa70d46a195d5382d288c477fb016"
"checksum inotify 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "40b54539f3910d6f84fbf9a643efd6e3aa6e4f001426c0329576128255994718"
"checksum inotify-sys 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "e74a1aa87c59aeff6ef2cc2fa62d41bc43f54952f55652656b18a02fd5e356c0"
diff --git a/Cargo.toml b/Cargo.toml
index ab6ee81..7cbe2a2 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -10,6 +10,7 @@ indicatif = "0.9.0"
console = "0.6.2"
notify = "4.0.0"
toml = "0.4.10"
+regex = "1.1.6"
serde = {version = "1.0.10", features = ["derive"]}
[[bin]]
@@ -18,3 +19,4 @@ path = "src/main.rs"
[dev-dependencies]
assert_cmd = "0.11.0"
+glob = "0.3.0"
diff --git a/exercises/enums/enums1.rs b/exercises/enums/enums1.rs
index ee665db..a2223d3 100644
--- a/exercises/enums/enums1.rs
+++ b/exercises/enums/enums1.rs
@@ -1,6 +1,8 @@
// enums1.rs
// Make me compile! Execute `rustlings hint enums1` for hints!
+// I AM NOT DONE
+
#[derive(Debug)]
enum Message {
// TODO: define a few types of messages as used below
diff --git a/exercises/enums/enums2.rs b/exercises/enums/enums2.rs
index f9eb8bd..52ccb22 100644
--- a/exercises/enums/enums2.rs
+++ b/exercises/enums/enums2.rs
@@ -1,6 +1,8 @@
// enums2.rs
// Make me compile! Execute `rustlings hint enums2` for hints!
+// I AM NOT DONE
+
#[derive(Debug)]
enum Message {
// TODO: define the different variants used below
diff --git a/exercises/enums/enums3.rs b/exercises/enums/enums3.rs
index c5d81bf..bb7dfb4 100644
--- a/exercises/enums/enums3.rs
+++ b/exercises/enums/enums3.rs
@@ -1,6 +1,8 @@
// enums3.rs
// Address all the TODOs to make the tests pass!
+// I AM NOT DONE
+
enum Message {
// TODO: implement the message variant types based on their usage below
}
diff --git a/exercises/error_handling/errors1.rs b/exercises/error_handling/errors1.rs
index f585e29..9c24d85 100644
--- a/exercises/error_handling/errors1.rs
+++ b/exercises/error_handling/errors1.rs
@@ -6,6 +6,8 @@
// this function to have.
// Execute `rustlings hint errors1` for hints!
+// I AM NOT DONE
+
pub fn generate_nametag_text(name: String) -> Option<String> {
if name.len() > 0 {
Some(format!("Hi! My name is {}", name))
diff --git a/exercises/error_handling/errors2.rs b/exercises/error_handling/errors2.rs
index 5ac6339..aad3a93 100644
--- a/exercises/error_handling/errors2.rs
+++ b/exercises/error_handling/errors2.rs
@@ -16,6 +16,8 @@
// There are at least two ways to implement this that are both correct-- but
// one is a lot shorter! Execute `rustlings hint errors2` for hints to both ways.
+// I AM NOT DONE
+
use std::num::ParseIntError;
pub fn total_cost(item_quantity: &str) -> Result<i32, ParseIntError> {
diff --git a/exercises/error_handling/errors3.rs b/exercises/error_handling/errors3.rs
index d9ec133..460ac5c 100644
--- a/exercises/error_handling/errors3.rs
+++ b/exercises/error_handling/errors3.rs
@@ -4,6 +4,8 @@
// Why not? What should we do to fix it?
// Execute `rustlings hint errors3` for hints!
+// I AM NOT DONE
+
use std::num::ParseIntError;
fn main() {
diff --git a/exercises/error_handling/errorsn.rs b/exercises/error_handling/errorsn.rs
index 2f3566b..fc25308 100644
--- a/exercises/error_handling/errorsn.rs
+++ b/exercises/error_handling/errorsn.rs
@@ -15,6 +15,8 @@
//
// Execute `rustlings hint errors4` for hints :)
+// I AM NOT DONE
+
use std::error;
use std::fmt;
use std::io;
diff --git a/exercises/error_handling/option1.rs b/exercises/error_handling/option1.rs
index e334e93..5d81b15 100644
--- a/exercises/error_handling/option1.rs
+++ b/exercises/error_handling/option1.rs
@@ -4,6 +4,8 @@
// on `None`. Handle this in a more graceful way than calling `unwrap`!
// Execute `rustlings hint option1` for hints :)
+// I AM NOT DONE
+
pub fn pop_too_much() -> bool {
let mut list = vec![3];
diff --git a/exercises/error_handling/result1.rs b/exercises/error_handling/result1.rs
index c3f2d6e..352a6c2 100644
--- a/exercises/error_handling/result1.rs
+++ b/exercises/error_handling/result1.rs
@@ -1,6 +1,8 @@
// result1.rs
// Make this test pass! Execute `rustlings hint option2` for hints :)
+// I AM NOT DONE
+
#[derive(PartialEq, Debug)]
struct PositiveNonzeroInteger(u64);
diff --git a/exercises/functions/functions1.rs b/exercises/functions/functions1.rs
index 313fe1e..49d48e9 100644
--- a/exercises/functions/functions1.rs
+++ b/exercises/functions/functions1.rs
@@ -1,6 +1,8 @@
// functions1.rs
// Make me compile! Execute `rustlings hint function1` for hints :)
+// I AM NOT DONE
+
fn main() {
call_me();
}
diff --git a/exercises/functions/functions2.rs b/exercises/functions/functions2.rs
index 9bb3d76..108ba38 100644
--- a/exercises/functions/functions2.rs
+++ b/exercises/functions/functions2.rs
@@ -1,6 +1,8 @@
// functions2.rs
// Make me compile! Execute `rustlings hint functions2` for hints :)
+// I AM NOT DONE
+
fn main() {
call_me(3);
}
diff --git a/exercises/functions/functions3.rs b/exercises/functions/functions3.rs
index b2e90f6..e3c1bf7 100644
--- a/exercises/functions/functions3.rs
+++ b/exercises/functions/functions3.rs
@@ -1,6 +1,8 @@
// functions3.rs
// Make me compile! Execute `rustlings hint functions3` for hints :)
+// I AM NOT DONE
+
fn main() {
call_me();
}
diff --git a/exercises/functions/functions4.rs b/exercises/functions/functions4.rs
index 78fc27b..6bf46f0 100644
--- a/exercises/functions/functions4.rs
+++ b/exercises/functions/functions4.rs
@@ -4,6 +4,8 @@
// This store is having a sale where if the price is an even number, you get
// 10 (money unit) off, but if it's an odd number, it's 3 (money unit) less.
+// I AM NOT DONE
+
fn main() {
let original_price = 51;
println!("Your sale price is {}", sale_price(original_price));
diff --git a/exercises/functions/functions5.rs b/exercises/functions/functions5.rs
index c7841a6..d22aa6c 100644
--- a/exercises/functions/functions5.rs
+++ b/exercises/functions/functions5.rs
@@ -1,6 +1,8 @@
// functions5.rs
// Make me compile! Execute `rustlings hint functions5` for hints :)
+// I AM NOT DONE
+
fn main() {
let answer = square(3);
println!("The answer is {}", answer);
diff --git a/exercises/if/if1.rs b/exercises/if/if1.rs
index bce052a..9086754 100644
--- a/exercises/if/if1.rs
+++ b/exercises/if/if1.rs
@@ -1,5 +1,7 @@
// if1.rs
+// I AM NOT DONE
+
pub fn bigger(a: i32, b: i32) -> i32 {
// Complete this function to return the bigger number!
// Do not use:
diff --git a/exercises/macros/macros1.rs b/exercises/macros/macros1.rs
index 0bc2a69..ed0dac8 100644
--- a/exercises/macros/macros1.rs
+++ b/exercises/macros/macros1.rs
@@ -1,6 +1,8 @@
// macros1.rs
// Make me compile! Execute `rustlings hint macros1` for hints :)
+// I AM NOT DONE
+
macro_rules! my_macro {
() => {
println!("Check out my macro!");
diff --git a/exercises/macros/macros2.rs b/exercises/macros/macros2.rs
index c4b4510..d0be123 100644
--- a/exercises/macros/macros2.rs
+++ b/exercises/macros/macros2.rs
@@ -1,6 +1,8 @@
// macros2.rs
// Make me compile! Execute `rustlings hint macros2` for hints :)
+// I AM NOT DONE
+
fn main() {
my_macro!();
}
diff --git a/exercises/macros/macros3.rs b/exercises/macros/macros3.rs
index 9b08adc..93a4311 100644
--- a/exercises/macros/macros3.rs
+++ b/exercises/macros/macros3.rs
@@ -2,6 +2,8 @@
// Make me compile, without taking the macro out of the module!
// Execute `rustlings hint macros3` for hints :)
+// I AM NOT DONE
+
mod macros {
macro_rules! my_macro {
() => {
diff --git a/exercises/macros/macros4.rs b/exercises/macros/macros4.rs
index 2ecba7d..3a74807 100644
--- a/exercises/macros/macros4.rs
+++ b/exercises/macros/macros4.rs
@@ -1,6 +1,8 @@
// macros4.rs
// Make me compile! Execute `rustlings hint macros4` for hints :)
+// I AM NOT DONE
+
macro_rules! my_macro {
() => {
println!("Check out my macro!");
diff --git a/exercises/modules/modules1.rs b/exercises/modules/modules1.rs
index c50d62f..812dfee 100644
--- a/exercises/modules/modules1.rs
+++ b/exercises/modules/modules1.rs
@@ -1,6 +1,8 @@
// modules1.rs
// Make me compile! Execute `rustlings hint modules1` for hints :)
+// I AM NOT DONE
+
mod sausage_factory {
fn make_sausage() {
println!("sausage!");
diff --git a/exercises/modules/modules2.rs b/exercises/modules/modules2.rs
index c3bf4f7..fde439d 100644
--- a/exercises/modules/modules2.rs
+++ b/exercises/modules/modules2.rs
@@ -1,6 +1,8 @@
// modules2.rs
// Make me compile! Execute `rustlings hint modules2` for hints :)
+// I AM NOT DONE
+
mod delicious_snacks {
use self::fruits::PEAR as fruit;
use self::veggies::CUCUMBER as veggie;
diff --git a/exercises/move_semantics/move_semantics1.rs b/exercises/move_semantics/move_semantics1.rs
index a69ec60..e2f5876 100644
--- a/exercises/move_semantics/move_semantics1.rs
+++ b/exercises/move_semantics/move_semantics1.rs
@@ -1,6 +1,8 @@
// move_semantics1.rs
// Make me compile! Execute `rustlings hint move_semantics1` for hints :)
+// I AM NOT DONE
+
fn main() {
let vec0 = Vec::new();
diff --git a/exercises/move_semantics/move_semantics2.rs b/exercises/move_semantics/move_semantics2.rs
index b945042..9233bb7 100644
--- a/exercises/move_semantics/move_semantics2.rs
+++ b/exercises/move_semantics/move_semantics2.rs
@@ -2,6 +2,8 @@
// Make me compile without changing line 10!
// Execute `rustlings hint move_semantics2` for hints :)
+// I AM NOT DONE
+
fn main() {
let vec0 = Vec::new();
diff --git a/exercises/move_semantics/move_semantics3.rs b/exercises/move_semantics/move_semantics3.rs
index 3f7958f..43fef74 100644
--- a/exercises/move_semantics/move_semantics3.rs
+++ b/exercises/move_semantics/move_semantics3.rs
@@ -3,6 +3,8 @@
// (no lines with multiple semicolons necessary!)
// Execute `rustlings hint move_semantics3` for hints :)
+// I AM NOT DONE
+
fn main() {
let vec0 = Vec::new();
diff --git a/exercises/move_semantics/move_semantics4.rs b/exercises/move_semantics/move_semantics4.rs
index 2143b81..a1c4a41 100644
--- a/exercises/move_semantics/move_semantics4.rs
+++ b/exercises/move_semantics/move_semantics4.rs
@@ -4,6 +4,8 @@
// freshly created vector from fill_vec to its caller.
// Execute `rustlings hint move_semantics4` for hints!
+// I AM NOT DONE
+
fn main() {
let vec0 = Vec::new();
diff --git a/exercises/primitive_types/primitive_types1.rs b/exercises/primitive_types/primitive_types1.rs
index c3d11fe..0912139 100644
--- a/exercises/primitive_types/primitive_types1.rs
+++ b/exercises/primitive_types/primitive_types1.rs
@@ -2,6 +2,8 @@
// 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/primitive_types/primitive_types2.rs b/exercises/primitive_types/primitive_types2.rs
index f5c8f87..6576a4d 100644
--- a/exercises/primitive_types/primitive_types2.rs
+++ b/exercises/primitive_types/primitive_types2.rs
@@ -2,6 +2,8 @@
// 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/primitive_types/primitive_types3.rs b/exercises/primitive_types/primitive_types3.rs
index e58c76c..dfd6351 100644
--- a/exercises/primitive_types/primitive_types3.rs
+++ b/exercises/primitive_types/primitive_types3.rs
@@ -2,6 +2,8 @@
// Create an array with at least 100 elements in it where the ??? is.
// Execute `rustlings hint primitive_types3` for hints!
+// I AM NOT DONE
+
fn main() {
let a = ???
diff --git a/exercises/primitive_types/primitive_types4.rs b/exercises/primitive_types/primitive_types4.rs
index 2efa58a..ff1e279 100644
--- a/exercises/primitive_types/primitive_types4.rs
+++ b/exercises/primitive_types/primitive_types4.rs
@@ -2,6 +2,8 @@
// Get a slice out of Array a where the ??? is so that the `if` statement
// returns true. Execute `rustlings hint primitive_types4` for hints!!
+// I AM NOT DONE
+
#[test]
fn main() {
let a = [1, 2, 3, 4, 5];
diff --git a/exercises/primitive_types/primitive_types5.rs b/exercises/primitive_types/primitive_types5.rs
index 6ab1c21..680d8d2 100644
--- a/exercises/primitive_types/primitive_types5.rs
+++ b/exercises/primitive_types/primitive_types5.rs
@@ -2,6 +2,8 @@
// Destructure the `cat` tuple so that the println will work.
// Execute `rustlings hint primitive_types5` for hints!
+// I AM NOT DONE
+
fn main() {
let cat = ("Furry McFurson", 3.5);
let /* your pattern here */ = cat;
diff --git a/exercises/primitive_types/primitive_types6.rs b/exercises/primitive_types/primitive_types6.rs
index 219a53e..2bc817e 100644
--- a/exercises/primitive_types/primitive_types6.rs
+++ b/exercises/primitive_types/primitive_types6.rs
@@ -3,6 +3,8 @@
// You can put this right into the `println!` where the ??? is.
// Execute `rustlings hint primitive_types6` for hints!
+// I AM NOT DONE
+
fn main() {
let numbers = (1, 2, 3);
println!("The second number is {}", ???);
diff --git a/exercises/standard_library_types/arc1.rs b/exercises/standard_library_types/arc1.rs
index 5aa02d9..04c169f 100644
--- a/exercises/standard_library_types/arc1.rs
+++ b/exercises/standard_library_types/arc1.rs
@@ -4,6 +4,8 @@
// somewhere. Try not to create any copies of the `numbers` Vec!
// Execute `rustlings help arc1` for hints :)
+// I AM NOT DONE
+
use std::sync::Arc;
use std::thread;
diff --git a/exercises/standard_library_types/iterators2.rs b/exercises/standard_library_types/iterators2.rs
index e70d5b6..a1274a2 100644
--- a/exercises/standard_library_types/iterators2.rs
+++ b/exercises/standard_library_types/iterators2.rs
@@ -5,6 +5,8 @@
// Step 3. Apply the `capitalize_first` function again to a list, but try and ensure it returns a single string
// As always, there are hints if you execute `rustlings hint iterators2`!
+// I AM NOT DONE
+
pub fn capitalize_first(input: &str) -> String {
let mut c = input.chars();
match c.next() {
diff --git a/exercises/standard_library_types/iterators3.rs b/exercises/standard_library_types/iterators3.rs
index d5a5afa..353cea6 100644
--- a/exercises/standard_library_types/iterators3.rs
+++ b/exercises/standard_library_types/iterators3.rs
@@ -7,6 +7,8 @@
// Execute `rustlings hint iterators3` to get some hints!
// Have fun :-)
+// I AM NOT DONE
+
#[derive(Debug, PartialEq, Eq)]
pub enum DivisionError {
NotDivisible(NotDivisibleError),
diff --git a/exercises/standard_library_types/iterators4.rs b/exercises/standard_library_types/iterators4.rs
index bcb7f3c..b945613 100644
--- a/exercises/standard_library_types/iterators4.rs
+++ b/exercises/standard_library_types/iterators4.rs
@@ -1,5 +1,7 @@
// iterators4.rs
+// I AM NOT DONE
+
pub fn factorial(num: u64) -> u64 {
// Complete this function to return factorial of num
// Do not use:
diff --git a/exercises/strings/strings1.rs b/exercises/strings/strings1.rs
index 0faf86e..8090244 100644
--- a/exercises/strings/strings1.rs
+++ b/exercises/strings/strings1.rs
@@ -2,6 +2,8 @@
// Make me compile without changing the function signature!
// Execute `rustlings hint strings1` for hints ;)
+// I AM NOT DONE
+
fn main() {
let answer = current_favorite_color();
println!("My current favorite color is {}", answer);
diff --git a/exercises/strings/strings2.rs b/exercises/strings/strings2.rs
index bd0f35c..5a2ce74 100644
--- a/exercises/strings/strings2.rs
+++ b/exercises/strings/strings2.rs
@@ -2,6 +2,8 @@
// Make me compile without changing the function signature!
// Execute `rustlings hint strings2` for hints :)
+// 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/structs/structs1.rs b/exercises/structs/structs1.rs
index 138b3fd..6d0b2f4 100644
--- a/exercises/structs/structs1.rs
+++ b/exercises/structs/structs1.rs
@@ -1,6 +1,8 @@
// structs1.rs
// Address all the TODOs to make the tests pass!
+// I AM NOT DONE
+
struct ColorClassicStruct {
// TODO: Something goes here
}
diff --git a/exercises/structs/structs2.rs b/exercises/structs/structs2.rs
index db381e7..0699137 100644
--- a/exercises/structs/structs2.rs
+++ b/exercises/structs/structs2.rs
@@ -2,6 +2,8 @@
// Address all the TODOs to make the tests pass!
// No hints, just do it!
+// I AM NOT DONE
+
#[derive(Debug)]
struct Order {
name: String,
diff --git a/exercises/test1.rs b/exercises/test1.rs
index 6c27355..3e13ce5 100644
--- a/exercises/test1.rs
+++ b/exercises/test1.rs
@@ -7,6 +7,8 @@
// more than 40 at once, each apple only costs 1! Write a function that calculates
// the price of an order of apples given the order amount. No hints this time!
+// I AM NOT DONE
+
// Put your function here!
// fn ..... {
diff --git a/exercises/test2.rs b/exercises/test2.rs
index 7fe81c6..d01606c 100644
--- a/exercises/test2.rs
+++ b/exercises/test2.rs
@@ -7,6 +7,8 @@
// you think each value is. That is, add either `string_slice` or `string`
// before the parentheses on each line. If you're right, it will compile!
+// I AM NOT DONE
+
fn string_slice(arg: &str) {
println!("{}", arg);
}
diff --git a/exercises/test3.rs b/exercises/test3.rs
index 9a72118..f94c36f 100644
--- a/exercises/test3.rs
+++ b/exercises/test3.rs
@@ -7,6 +7,8 @@
// we expect to get when we call `times_two` with a negative number.
// No hints, you can do this :)
+// I AM NOT DONE
+
pub fn times_two(num: i32) -> i32 {
num * 2
}
diff --git a/exercises/test4.rs b/exercises/test4.rs
index e50f1b0..c543a64 100644
--- a/exercises/test4.rs
+++ b/exercises/test4.rs
@@ -5,6 +5,8 @@
// Write a macro that passes the test! No hints this time, you can do it!
+// I AM NOT DONE
+
fn main() {
if my_macro!("world!") != "Hello world!" {
panic!("Oh no! Wrong output!");
diff --git a/exercises/tests/tests1.rs b/exercises/tests/tests1.rs
index 5eb918a..b37cefa 100644
--- a/exercises/tests/tests1.rs
+++ b/exercises/tests/tests1.rs
@@ -6,6 +6,8 @@
// This test has a problem with it -- make the test compile! Make the test
// pass! Make the test fail! Execute `rustlings hint tests1` for hints :)
+// I AM NOT DONE
+
#[cfg(test)]
mod tests {
#[test]
diff --git a/exercises/tests/tests2.rs b/exercises/tests/tests2.rs
index 044ed9e..0d981ad 100644
--- a/exercises/tests/tests2.rs
+++ b/exercises/tests/tests2.rs
@@ -2,6 +2,8 @@
// This test has a problem with it -- make the test compile! Make the test
// pass! Make the test fail! Execute `rustlings hint tests2` for hints :)
+// I AM NOT DONE
+
#[cfg(test)]
mod tests {
#[test]
diff --git a/exercises/tests/tests3.rs b/exercises/tests/tests3.rs
index 0f244b3..693b8aa 100644
--- a/exercises/tests/tests3.rs
+++ b/exercises/tests/tests3.rs
@@ -4,6 +4,8 @@
// we expect to get when we call `is_even(5)`.
// Execute `rustlings hint tests3` for hints :)
+// I AM NOT DONE
+
pub fn is_even(num: i32) -> bool {
num % 2 == 0
}
diff --git a/exercises/threads/threads1.rs b/exercises/threads/threads1.rs
index 3308321..288ddd1 100644
--- a/exercises/threads/threads1.rs
+++ b/exercises/threads/threads1.rs
@@ -5,6 +5,8 @@
// of "waiting..." and the program ends without timing out when running,
// you've got it :)
+// I AM NOT DONE
+
use std::sync::Arc;
use std::thread;
use std::time::Duration;
diff --git a/exercises/variables/variables1.rs b/exercises/variables/variables1.rs
index 65452c2..5ddc37d 100644
--- a/exercises/variables/variables1.rs
+++ b/exercises/variables/variables1.rs
@@ -1,6 +1,13 @@
// variables1.rs
// Make me compile! Execute the command `rustlings hint variables1` if you want a hint :)
+// About this `I AM NOT DONE` thing:
+// 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, you the `I AM NOT DONE` comment below.
+
+// I AM NOT DONE
+
fn main() {
x = 5;
println!("x has the value {}", x);
diff --git a/exercises/variables/variables2.rs b/exercises/variables/variables2.rs
index 388b05d..7774a8f 100644
--- a/exercises/variables/variables2.rs
+++ b/exercises/variables/variables2.rs
@@ -1,6 +1,8 @@
// variables2.rs
// Make me compile! Execute the command `rustlings hint variables2` if you want a hint :)
+// I AM NOT DONE
+
fn main() {
let x;
if x == 10 {
diff --git a/exercises/variables/variables3.rs b/exercises/variables/variables3.rs
index f2c9c63..07b1a52 100644
--- a/exercises/variables/variables3.rs
+++ b/exercises/variables/variables3.rs
@@ -1,6 +1,8 @@
// variables3.rs
// Make me compile! Execute the command `rustlings hint variables3` if you want a hint :)
+// I AM NOT DONE
+
fn main() {
let x = 3;
println!("Number {}", x);
diff --git a/exercises/variables/variables4.rs b/exercises/variables/variables4.rs
index f1ed6ef..77f1e9a 100644
--- a/exercises/variables/variables4.rs
+++ b/exercises/variables/variables4.rs
@@ -1,6 +1,8 @@
// variables4.rs
// Make me compile! Execute the command `rustlings hint variables4` if you want a hint :)
+// I AM NOT DONE
+
fn main() {
let x: i32;
println!("Number {}", x);
diff --git a/src/exercise.rs b/src/exercise.rs
index 0e8a199..be59284 100644
--- a/src/exercise.rs
+++ b/src/exercise.rs
@@ -1,16 +1,20 @@
+use regex::Regex;
use serde::Deserialize;
use std::fmt::{self, Display, Formatter};
-use std::fs::remove_file;
+use std::fs::{remove_file, File};
+use std::io::Read;
use std::path::PathBuf;
use std::process::{self, Command, Output};
const RUSTC_COLOR_ARGS: &[&str] = &["--color", "always"];
+const I_AM_DONE_REGEX: &str = r"(?m)^\s*///?\s*I\s+AM\s+NOT\s+DONE";
+const CONTEXT: usize = 2;
fn temp_file() -> String {
format!("./temp_{}", process::id())
}
-#[derive(Deserialize)]
+#[derive(Deserialize, Copy, Clone)]
#[serde(rename_all = "lowercase")]
pub enum Mode {
Compile,
@@ -30,6 +34,19 @@ pub struct Exercise {
pub hint: String,
}
+#[derive(PartialEq, Debug)]
+pub enum State {
+ Done,
+ Pending(Vec<ContextLine>),
+}
+
+#[derive(PartialEq, Debug)]
+pub struct ContextLine {
+ pub line: String,
+ pub number: usize,
+ pub important: bool,
+}
+
impl Exercise {
pub fn compile(&self) -> Output {
match self.mode {
@@ -54,6 +71,48 @@ impl Exercise {
pub fn clean(&self) {
let _ignored = remove_file(&temp_file());
}
+
+ pub fn state(&self) -> State {
+ let mut source_file =
+ File::open(&self.path).expect("We were unable to open the exercise file!");
+
+ let source = {
+ let mut s = String::new();
+ source_file
+ .read_to_string(&mut s)
+ .expect("We were unable to read the exercise file!");
+ s
+ };
+
+ let re = Regex::new(I_AM_DONE_REGEX).unwrap();
+
+ if !re.is_match(&source) {
+ return State::Done;
+ }
+
+ let matched_line_index = source
+ .lines()
+ .enumerate()
+ .filter_map(|(i, line)| if re.is_match(line) { Some(i) } else { None })
+ .next()
+ .expect("This should not happen at all");
+
+ let min_line = ((matched_line_index as i32) - (CONTEXT as i32)).max(0) as usize;
+ let max_line = matched_line_index + CONTEXT;
+
+ let context = source
+ .lines()
+ .enumerate()
+ .filter(|&(i, _)| i >= min_line && i <= max_line)
+ .map(|(i, line)| ContextLine {
+ line: line.to_string(),
+ number: i + 1,
+ important: i == matched_line_index,
+ })
+ .collect();
+
+ State::Pending(context)
+ }
}
impl Display for Exercise {
@@ -65,7 +124,6 @@ impl Display for Exercise {
#[cfg(test)]
mod test {
use super::*;
- use std::fs::File;
use std::path::Path;
#[test]
@@ -80,4 +138,53 @@ mod test {
exercise.clean();
assert!(!Path::new(&temp_file()).exists());
}
+
+ #[test]
+ fn test_pending_state() {
+ let exercise = Exercise {
+ path: PathBuf::from("tests/fixture/state/pending_exercise.rs"),
+ mode: Mode::Compile,
+ };
+
+ 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, State::Pending(expected));
+ }
+
+ #[test]
+ fn test_finished_exercise() {
+ let exercise = Exercise {
+ path: PathBuf::from("tests/fixture/state/finished_exercise.rs"),
+ mode: Mode::Compile,
+ };
+
+ assert_eq!(exercise.state(), State::Done);
+ }
}
diff --git a/src/main.rs b/src/main.rs
index 875f767..5a4af53 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -127,11 +127,11 @@ fn watch(exercises: &[Exercise]) -> notify::Result<()> {
DebouncedEvent::Create(b) | DebouncedEvent::Chmod(b) | DebouncedEvent::Write(b) => {
if b.extension() == Some(OsStr::new("rs")) && b.exists() {
let filepath = b.as_path().canonicalize().unwrap();
- let exercise = exercises
+ let pending_exercises = exercises
.iter()
.skip_while(|e| !filepath.ends_with(&e.path));
clear_screen();
- let _ignored = verify(exercise);
+ let _ignored = verify(pending_exercises);
}
}
_ => {}
diff --git a/src/run.rs b/src/run.rs
index 1484351..1f777ec 100644
--- a/src/run.rs
+++ b/src/run.rs
@@ -5,7 +5,9 @@ use indicatif::ProgressBar;
pub fn run(exercise: &Exercise) -> Result<(), ()> {
match exercise.mode {
- Mode::Test => test(exercise)?,
+ Mode::Test => {
+ test(exercise)?;
+ }
Mode::Compile => compile_and_run(exercise)?,
}
Ok(())
diff --git a/src/verify.rs b/src/verify.rs
index d066afa..020102e 100644
--- a/src/verify.rs
+++ b/src/verify.rs
@@ -1,18 +1,21 @@
-use crate::exercise::{Exercise, Mode};
+use crate::exercise::{ContextLine, Exercise, Mode, State};
use console::{style, Emoji};
use indicatif::ProgressBar;
pub fn verify<'a>(start_at: impl IntoIterator<Item = &'a Exercise>) -> Result<(), ()> {
for exercise in start_at {
- match exercise.mode {
+ let is_done = match exercise.mode {
Mode::Test => test(&exercise)?,
Mode::Compile => compile_only(&exercise)?,
+ };
+ if !is_done {
+ return Err(());
}
}
Ok(())
}
-fn compile_only(exercise: &Exercise) -> Result<(), ()> {
+fn compile_only(exercise: &Exercise) -> Result<bool, ()> {
let progress_bar = ProgressBar::new_spinner();
progress_bar.set_message(format!("Compiling {}...", exercise).as_str());
progress_bar.enable_steady_tick(100);
@@ -22,7 +25,12 @@ fn compile_only(exercise: &Exercise) -> Result<(), ()> {
let formatstr = format!("{} Successfully compiled {}!", Emoji("✅", "✓"), exercise);
println!("{}", style(formatstr).green());
exercise.clean();
- Ok(())
+ if let State::Pending(context) = exercise.state() {
+ print_everything_looks_good(exercise.mode, context);
+ Ok(false)
+ } else {
+ Ok(true)
+ }
} else {
let formatstr = format!(
"{} Compilation of {} failed! Compiler error message:\n",
@@ -36,7 +44,7 @@ fn compile_only(exercise: &Exercise) -> Result<(), ()> {
}
}
-pub fn test(exercise: &Exercise) -> Result<(), ()> {
+pub fn test(exercise: &Exercise) -> Result<bool, ()> {
let progress_bar = ProgressBar::new_spinner();
progress_bar.set_message(format!("Testing {}...", exercise).as_str());
progress_bar.enable_steady_tick(100);
@@ -52,7 +60,12 @@ pub fn test(exercise: &Exercise) -> Result<(), ()> {
let formatstr = format!("{} Successfully tested {}!", Emoji("✅", "✓"), exercise);
println!("{}", style(formatstr).green());
exercise.clean();
- Ok(())
+ if let State::Pending(context) = exercise.state() {
+ print_everything_looks_good(exercise.mode, context);
+ Ok(false)
+ } else {
+ Ok(true)
+ }
} else {
let formatstr = format!(
"{} Testing of {} failed! Please try again. Here's the output:",
@@ -77,3 +90,34 @@ pub fn test(exercise: &Exercise) -> Result<(), ()> {
Err(())
}
}
+
+fn print_everything_looks_good(mode: Mode, context: Vec<ContextLine>) {
+ let success_msg = match mode {
+ Mode::Compile => "The code is compiling!",
+ Mode::Test => "The code is compiling, and the tests pass!",
+ };
+
+ println!("");
+ println!("🎉 🎉 {} 🎉 🎉", success_msg);
+ println!("");
+ println!("You can keep working on this exercise,");
+ println!(
+ "or jump into the next one by removing the {} comment:",
+ style("`I AM NOT DONE`").bold()
+ );
+ println!("");
+ for context_line in context {
+ let formatted_line = if context_line.important {
+ format!("{}", style(context_line.line).bold())
+ } else {
+ format!("{}", context_line.line)
+ };
+
+ println!(
+ "{:>2} {} {}",
+ style(context_line.number).blue().bold(),
+ style("|").blue(),
+ formatted_line
+ );
+ }
+}
diff --git a/tests/fixture/state/finished_exercise.rs b/tests/fixture/state/finished_exercise.rs
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/fixture/state/finished_exercise.rs
diff --git a/tests/fixture/state/pending_exercise.rs b/tests/fixture/state/pending_exercise.rs
new file mode 100644
index 0000000..f579d0b
--- /dev/null
+++ b/tests/fixture/state/pending_exercise.rs
@@ -0,0 +1,7 @@
+// fake_exercise
+
+// I AM NOT DONE
+
+fn main() {
+
+}
diff --git a/tests/integration_tests.rs b/tests/integration_tests.rs
index 24268ec..32f4341 100644
--- a/tests/integration_tests.rs
+++ b/tests/integration_tests.rs
@@ -1,4 +1,7 @@
use assert_cmd::prelude::*;
+use glob::glob;
+use std::fs::File;
+use std::io::Read;
use std::process::Command;
#[test]
@@ -115,4 +118,20 @@ fn get_hint_for_single_test() {
.assert()
.code(0)
.stdout("Hello!\n");
+
+#[test]
+fn all_exercises_require_confirmation() {
+ for exercise in glob("exercises/**/*.rs").unwrap() {
+ let path = exercise.unwrap();
+ 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().expect(&format!(
+ "There should be an `I AM NOT DONE` annotation in {:?}",
+ path
+ ));
+ }
}