summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2019-11-11 15:59:35 +0000
committerbors <bors@rust-lang.org>2019-11-11 15:59:35 +0000
commit88ec6f6b1645599f77643ee5e6f5dd16cd67c943 (patch)
tree36442292a4e3b0ae07072b0dfaf59a3bc799a397
parenta47a62172a5d4c479d242498b9b140b3111fb7c6 (diff)
parent2cdd61294f0d9a53775ee24ad76435bec8a21e60 (diff)
Auto merge of #230 - jrvidal:master, r=fmoko
Changes the execution mode for `watch`, asking for user input We've [observed](https://hackmd.io/-cK6aPhnTwiCiI7u6k0xug?both) that learners can get confused when they do get everything right, but they _still_ get errors... which come from the next exercise, no the one they just edited. This PR changes it so they have to confirm they want to move forward by removing the `I AM NOT DONE` comment. ![Screenshot at 2019-11-11 15:13:39](https://user-images.githubusercontent.com/1636604/68593566-0abd3900-0496-11ea-9e9d-6c43b91bf21d.png) * [ ] The particular string is of course subject to bikeshed. ### Alternatives/doubts * The coolest solution I could imagine would involve a proc-macro attribute `#![ready(false)]` that they could edit once they're done, but it's a bit complicated to set up. * For now I've put `I AM NOT DONE` everywhere, I think it's what make more sense.
-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.rs20
60 files changed, 309 insertions, 12 deletions
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 8b14f29..f490928 100644
--- a/exercises/enums/enums1.rs
+++ b/exercises/enums/enums1.rs
@@ -1,6 +1,8 @@
// enums1.rs
// Make me compile! Scroll down 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 71ac839..8f04bd8 100644
--- a/exercises/enums/enums2.rs
+++ b/exercises/enums/enums2.rs
@@ -1,6 +1,8 @@
// enums2.rs
// Make me compile! Scroll down 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 8483234..a06f0ca 100644
--- a/exercises/error_handling/errors1.rs
+++ b/exercises/error_handling/errors1.rs
@@ -6,6 +6,8 @@
// this function to have.
// Scroll down 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 8b81207..315528f 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! Scroll down 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 31800fc..35cd5c3 100644
--- a/exercises/error_handling/errors3.rs
+++ b/exercises/error_handling/errors3.rs
@@ -3,6 +3,8 @@
// `total_cost` function from the previous exercise. It's not working though!
// Why not? What should we do to fix it? Scroll 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 c2b16ce..cc90801 100644
--- a/exercises/error_handling/errorsn.rs
+++ b/exercises/error_handling/errorsn.rs
@@ -15,6 +15,8 @@
//
// Scroll down 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 c5a4a64..4e432a6 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`!
// Scroll down 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 f9596e2..52972dc 100644
--- a/exercises/error_handling/result1.rs
+++ b/exercises/error_handling/result1.rs
@@ -1,6 +1,8 @@
// result1.rs
// Make this test pass! Scroll down 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 396dd56..b9312ae 100644
--- a/exercises/functions/functions1.rs
+++ b/exercises/functions/functions1.rs
@@ -1,6 +1,8 @@
// functions1.rs
// Make me compile! Scroll down for hints :)
+// I AM NOT DONE
+
fn main() {
call_me();
}
diff --git a/exercises/functions/functions2.rs b/exercises/functions/functions2.rs
index 1cf95c3..c5b4418 100644
--- a/exercises/functions/functions2.rs
+++ b/exercises/functions/functions2.rs
@@ -1,6 +1,8 @@
// functions2.rs
// Make me compile! Scroll down for hints :)
+// I AM NOT DONE
+
fn main() {
call_me(3);
}
diff --git a/exercises/functions/functions3.rs b/exercises/functions/functions3.rs
index b17543b..63bb586 100644
--- a/exercises/functions/functions3.rs
+++ b/exercises/functions/functions3.rs
@@ -1,6 +1,8 @@
// functions3.rs
// Make me compile! Scroll down for hints :)
+// I AM NOT DONE
+
fn main() {
call_me();
}
diff --git a/exercises/functions/functions4.rs b/exercises/functions/functions4.rs
index 5baca0e..91f6295 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 d9227c9..696edee 100644
--- a/exercises/functions/functions5.rs
+++ b/exercises/functions/functions5.rs
@@ -1,6 +1,8 @@
// functions5.rs
// Make me compile! Scroll down 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 6da09d3..da77e35 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 a7c78a5..f7b89e0 100644
--- a/exercises/macros/macros1.rs
+++ b/exercises/macros/macros1.rs
@@ -1,6 +1,8 @@
// macros1.rs
// Make me compile! Scroll down 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 bc2e56b..7fe9429 100644
--- a/exercises/macros/macros2.rs
+++ b/exercises/macros/macros2.rs
@@ -1,6 +1,8 @@
// macros2.rs
// Make me compile! Scroll down for hints :)
+// I AM NOT DONE
+
fn main() {
my_macro!();
}
diff --git a/exercises/macros/macros3.rs b/exercises/macros/macros3.rs
index 84c4308..e9ef7f3 100644
--- a/exercises/macros/macros3.rs
+++ b/exercises/macros/macros3.rs
@@ -1,6 +1,8 @@
// macros3.rs
// Make me compile, without taking the macro out of the module! Scroll down 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 d844bb0..2893a15 100644
--- a/exercises/macros/macros4.rs
+++ b/exercises/macros/macros4.rs
@@ -1,6 +1,8 @@
// macros4.rs
// Make me compile! Scroll down 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 0e092c5..3f44968 100644
--- a/exercises/modules/modules1.rs
+++ b/exercises/modules/modules1.rs
@@ -1,6 +1,8 @@
// modules1.rs
// Make me compile! Scroll down 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 3cfa36d..a3497cf 100644
--- a/exercises/modules/modules2.rs
+++ b/exercises/modules/modules2.rs
@@ -1,6 +1,8 @@
// modules2.rs
// Make me compile! Scroll down 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 ab855fc..625096d 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! Scroll down 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 f85b3ed..28749e2 100644
--- a/exercises/move_semantics/move_semantics2.rs
+++ b/exercises/move_semantics/move_semantics2.rs
@@ -1,6 +1,8 @@
// move_semantics2.rs
// Make me compile without changing line 10! Scroll down 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 8b91896..a5b41b4 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!)
// Scroll down 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 90930f0..b01e593 100644
--- a/exercises/move_semantics/move_semantics4.rs
+++ b/exercises/move_semantics/move_semantics4.rs
@@ -3,6 +3,8 @@
// in `fn main`, we instead create it within `fn fill_vec` and transfer the
// freshly created vector from fill_vec to its caller. Scroll 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 7ce2226..bf2716a 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.
// Scroll down 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 e1ccdbc..e675aa1 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. Scroll down 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 0d53c9c..d8527ee 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.
// Scroll down 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 854544f..8fe9a36 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.
// Scroll down 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 d610e5f..effd28a 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!
// Scroll down 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 b6d3366..ea523e7 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 below!
+// 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 c012795..8fc57f2 100644
--- a/exercises/standard_library_types/iterators3.rs
+++ b/exercises/standard_library_types/iterators3.rs
@@ -8,6 +8,8 @@
// a major hint.
// 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 13613a6..39be8bf 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 2e5088f..3335734 100644
--- a/exercises/strings/strings1.rs
+++ b/exercises/strings/strings1.rs
@@ -1,6 +1,8 @@
// strings1.rs
// Make me compile without changing the function signature! Scroll down 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 c77e16f..3890cfe 100644
--- a/exercises/strings/strings2.rs
+++ b/exercises/strings/strings2.rs
@@ -1,6 +1,8 @@
// strings2.rs
// Make me compile without changing the function signature! Scroll down 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 b11221f..1465e37 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! Scroll down for hints :)
+// I AM NOT DONE
+
#[cfg(test)]
mod tests {
#[test]
diff --git a/exercises/tests/tests2.rs b/exercises/tests/tests2.rs
index 6775d61..81e30e0 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! Scroll down for hints :)
+// I AM NOT DONE
+
#[cfg(test)]
mod tests {
#[test]
diff --git a/exercises/tests/tests3.rs b/exercises/tests/tests3.rs
index e10d2aa..af48095 100644
--- a/exercises/tests/tests3.rs
+++ b/exercises/tests/tests3.rs
@@ -3,6 +3,8 @@
// the test passes. Then write a second test that tests whether we get the result
// we expect to get when we call `is_even(5)`. Scroll down 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 7983668..d23553e 100644
--- a/exercises/threads/threads1.rs
+++ b/exercises/threads/threads1.rs
@@ -5,6 +5,8 @@
// of "waiting..." and the program ends without timing out the playground,
// 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 1cdd270..174ad80 100644
--- a/exercises/variables/variables1.rs
+++ b/exercises/variables/variables1.rs
@@ -1,6 +1,13 @@
// variables1.rs
// Make me compile! Scroll down for hints :)
+// 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 a0b4a37..e3c3b76 100644
--- a/exercises/variables/variables2.rs
+++ b/exercises/variables/variables2.rs
@@ -1,6 +1,8 @@
// variables2.rs
// Make me compile! Scroll down for hints :)
+// I AM NOT DONE
+
fn main() {
let x;
if x == 10 {
diff --git a/exercises/variables/variables3.rs b/exercises/variables/variables3.rs
index 165a277..7e3fd93 100644
--- a/exercises/variables/variables3.rs
+++ b/exercises/variables/variables3.rs
@@ -1,6 +1,8 @@
// variables3.rs
// Make me compile! Scroll down for hints :)
+// 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 71ebf0f..33eca9e 100644
--- a/exercises/variables/variables4.rs
+++ b/exercises/variables/variables4.rs
@@ -1,6 +1,8 @@
// variables4.rs
// Make me compile! Scroll down for hints :)
+// I AM NOT DONE
+
fn main() {
let x: i32;
println!("Number {}", x);
diff --git a/src/exercise.rs b/src/exercise.rs
index 6f526e7..f27b545 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,
@@ -28,6 +32,19 @@ pub struct Exercise {
pub mode: Mode,
}
+#[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 {
@@ -52,6 +69,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 {
@@ -63,7 +122,6 @@ impl Display for Exercise {
#[cfg(test)]
mod test {
use super::*;
- use std::fs::File;
use std::path::Path;
#[test]
@@ -76,4 +134,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 aad4cff..e42dff4 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -110,11 +110,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 3acaf90..897cd2a 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]
@@ -105,3 +108,20 @@ fn run_single_test_no_exercise() {
.assert()
.code(1);
}
+
+#[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
+ ));
+ }
+}