feat: improve `watch` execution mode
The `watch` command now requires user action to move to the next exercise. BREAKING CHANGE: this changes the behavior of `watch`.
This commit is contained in:
parent
a47a62172a
commit
2cdd61294f
|
@ -204,6 +204,11 @@ name = "fuchsia-zircon-sys"
|
||||||
version = "0.3.3"
|
version = "0.3.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
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]]
|
[[package]]
|
||||||
name = "indicatif"
|
name = "indicatif"
|
||||||
version = "0.9.0"
|
version = "0.9.0"
|
||||||
|
@ -605,8 +610,10 @@ dependencies = [
|
||||||
"assert_cmd 0.11.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"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)",
|
"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)",
|
"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)",
|
"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)",
|
"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)",
|
"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)",
|
"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-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 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 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 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 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"
|
"checksum inotify-sys 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "e74a1aa87c59aeff6ef2cc2fa62d41bc43f54952f55652656b18a02fd5e356c0"
|
||||||
|
|
|
@ -10,6 +10,7 @@ indicatif = "0.9.0"
|
||||||
console = "0.6.2"
|
console = "0.6.2"
|
||||||
notify = "4.0.0"
|
notify = "4.0.0"
|
||||||
toml = "0.4.10"
|
toml = "0.4.10"
|
||||||
|
regex = "1.1.6"
|
||||||
serde = {version = "1.0.10", features = ["derive"]}
|
serde = {version = "1.0.10", features = ["derive"]}
|
||||||
|
|
||||||
[[bin]]
|
[[bin]]
|
||||||
|
@ -18,3 +19,4 @@ path = "src/main.rs"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
assert_cmd = "0.11.0"
|
assert_cmd = "0.11.0"
|
||||||
|
glob = "0.3.0"
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
// enums1.rs
|
// enums1.rs
|
||||||
// Make me compile! Scroll down for hints!
|
// Make me compile! Scroll down for hints!
|
||||||
|
|
||||||
|
// I AM NOT DONE
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
enum Message {
|
enum Message {
|
||||||
// TODO: define a few types of messages as used below
|
// TODO: define a few types of messages as used below
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
// enums2.rs
|
// enums2.rs
|
||||||
// Make me compile! Scroll down for hints
|
// Make me compile! Scroll down for hints
|
||||||
|
|
||||||
|
// I AM NOT DONE
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
enum Message {
|
enum Message {
|
||||||
// TODO: define the different variants used below
|
// TODO: define the different variants used below
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
// enums3.rs
|
// enums3.rs
|
||||||
// Address all the TODOs to make the tests pass!
|
// Address all the TODOs to make the tests pass!
|
||||||
|
|
||||||
|
// I AM NOT DONE
|
||||||
|
|
||||||
enum Message {
|
enum Message {
|
||||||
// TODO: implement the message variant types based on their usage below
|
// TODO: implement the message variant types based on their usage below
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,8 @@
|
||||||
// this function to have.
|
// this function to have.
|
||||||
// Scroll down for hints!!!
|
// Scroll down for hints!!!
|
||||||
|
|
||||||
|
// I AM NOT DONE
|
||||||
|
|
||||||
pub fn generate_nametag_text(name: String) -> Option<String> {
|
pub fn generate_nametag_text(name: String) -> Option<String> {
|
||||||
if name.len() > 0 {
|
if name.len() > 0 {
|
||||||
Some(format!("Hi! My name is {}", name))
|
Some(format!("Hi! My name is {}", name))
|
||||||
|
|
|
@ -16,6 +16,8 @@
|
||||||
// There are at least two ways to implement this that are both correct-- but
|
// 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.
|
// one is a lot shorter! Scroll down for hints to both ways.
|
||||||
|
|
||||||
|
// I AM NOT DONE
|
||||||
|
|
||||||
use std::num::ParseIntError;
|
use std::num::ParseIntError;
|
||||||
|
|
||||||
pub fn total_cost(item_quantity: &str) -> Result<i32, ParseIntError> {
|
pub fn total_cost(item_quantity: &str) -> Result<i32, ParseIntError> {
|
||||||
|
|
|
@ -3,6 +3,8 @@
|
||||||
// `total_cost` function from the previous exercise. It's not working though!
|
// `total_cost` function from the previous exercise. It's not working though!
|
||||||
// Why not? What should we do to fix it? Scroll for hints!
|
// Why not? What should we do to fix it? Scroll for hints!
|
||||||
|
|
||||||
|
// I AM NOT DONE
|
||||||
|
|
||||||
use std::num::ParseIntError;
|
use std::num::ParseIntError;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
|
|
|
@ -15,6 +15,8 @@
|
||||||
//
|
//
|
||||||
// Scroll down for hints :)
|
// Scroll down for hints :)
|
||||||
|
|
||||||
|
// I AM NOT DONE
|
||||||
|
|
||||||
use std::error;
|
use std::error;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::io;
|
use std::io;
|
||||||
|
|
|
@ -4,6 +4,8 @@
|
||||||
// on `None`. Handle this in a more graceful way than calling `unwrap`!
|
// on `None`. Handle this in a more graceful way than calling `unwrap`!
|
||||||
// Scroll down for hints :)
|
// Scroll down for hints :)
|
||||||
|
|
||||||
|
// I AM NOT DONE
|
||||||
|
|
||||||
pub fn pop_too_much() -> bool {
|
pub fn pop_too_much() -> bool {
|
||||||
let mut list = vec![3];
|
let mut list = vec![3];
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
// result1.rs
|
// result1.rs
|
||||||
// Make this test pass! Scroll down for hints :)
|
// Make this test pass! Scroll down for hints :)
|
||||||
|
|
||||||
|
// I AM NOT DONE
|
||||||
|
|
||||||
#[derive(PartialEq, Debug)]
|
#[derive(PartialEq, Debug)]
|
||||||
struct PositiveNonzeroInteger(u64);
|
struct PositiveNonzeroInteger(u64);
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
// functions1.rs
|
// functions1.rs
|
||||||
// Make me compile! Scroll down for hints :)
|
// Make me compile! Scroll down for hints :)
|
||||||
|
|
||||||
|
// I AM NOT DONE
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
call_me();
|
call_me();
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
// functions2.rs
|
// functions2.rs
|
||||||
// Make me compile! Scroll down for hints :)
|
// Make me compile! Scroll down for hints :)
|
||||||
|
|
||||||
|
// I AM NOT DONE
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
call_me(3);
|
call_me(3);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
// functions3.rs
|
// functions3.rs
|
||||||
// Make me compile! Scroll down for hints :)
|
// Make me compile! Scroll down for hints :)
|
||||||
|
|
||||||
|
// I AM NOT DONE
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
call_me();
|
call_me();
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,8 @@
|
||||||
// This store is having a sale where if the price is an even number, you get
|
// 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.
|
// 10 (money unit) off, but if it's an odd number, it's 3 (money unit) less.
|
||||||
|
|
||||||
|
// I AM NOT DONE
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let original_price = 51;
|
let original_price = 51;
|
||||||
println!("Your sale price is {}", sale_price(original_price));
|
println!("Your sale price is {}", sale_price(original_price));
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
// functions5.rs
|
// functions5.rs
|
||||||
// Make me compile! Scroll down for hints :)
|
// Make me compile! Scroll down for hints :)
|
||||||
|
|
||||||
|
// I AM NOT DONE
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let answer = square(3);
|
let answer = square(3);
|
||||||
println!("The answer is {}", answer);
|
println!("The answer is {}", answer);
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
// if1.rs
|
// if1.rs
|
||||||
|
|
||||||
|
// I AM NOT DONE
|
||||||
|
|
||||||
pub fn bigger(a: i32, b: i32) -> i32 {
|
pub fn bigger(a: i32, b: i32) -> i32 {
|
||||||
// Complete this function to return the bigger number!
|
// Complete this function to return the bigger number!
|
||||||
// Do not use:
|
// Do not use:
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
// macros1.rs
|
// macros1.rs
|
||||||
// Make me compile! Scroll down for hints :)
|
// Make me compile! Scroll down for hints :)
|
||||||
|
|
||||||
|
// I AM NOT DONE
|
||||||
|
|
||||||
macro_rules! my_macro {
|
macro_rules! my_macro {
|
||||||
() => {
|
() => {
|
||||||
println!("Check out my macro!");
|
println!("Check out my macro!");
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
// macros2.rs
|
// macros2.rs
|
||||||
// Make me compile! Scroll down for hints :)
|
// Make me compile! Scroll down for hints :)
|
||||||
|
|
||||||
|
// I AM NOT DONE
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
my_macro!();
|
my_macro!();
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
// macros3.rs
|
// macros3.rs
|
||||||
// Make me compile, without taking the macro out of the module! Scroll down for hints :)
|
// Make me compile, without taking the macro out of the module! Scroll down for hints :)
|
||||||
|
|
||||||
|
// I AM NOT DONE
|
||||||
|
|
||||||
mod macros {
|
mod macros {
|
||||||
macro_rules! my_macro {
|
macro_rules! my_macro {
|
||||||
() => {
|
() => {
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
// macros4.rs
|
// macros4.rs
|
||||||
// Make me compile! Scroll down for hints :)
|
// Make me compile! Scroll down for hints :)
|
||||||
|
|
||||||
|
// I AM NOT DONE
|
||||||
|
|
||||||
macro_rules! my_macro {
|
macro_rules! my_macro {
|
||||||
() => {
|
() => {
|
||||||
println!("Check out my macro!");
|
println!("Check out my macro!");
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
// modules1.rs
|
// modules1.rs
|
||||||
// Make me compile! Scroll down for hints :)
|
// Make me compile! Scroll down for hints :)
|
||||||
|
|
||||||
|
// I AM NOT DONE
|
||||||
|
|
||||||
mod sausage_factory {
|
mod sausage_factory {
|
||||||
fn make_sausage() {
|
fn make_sausage() {
|
||||||
println!("sausage!");
|
println!("sausage!");
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
// modules2.rs
|
// modules2.rs
|
||||||
// Make me compile! Scroll down for hints :)
|
// Make me compile! Scroll down for hints :)
|
||||||
|
|
||||||
|
// I AM NOT DONE
|
||||||
|
|
||||||
mod delicious_snacks {
|
mod delicious_snacks {
|
||||||
use self::fruits::PEAR as fruit;
|
use self::fruits::PEAR as fruit;
|
||||||
use self::veggies::CUCUMBER as veggie;
|
use self::veggies::CUCUMBER as veggie;
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
// move_semantics1.rs
|
// move_semantics1.rs
|
||||||
// Make me compile! Scroll down for hints :)
|
// Make me compile! Scroll down for hints :)
|
||||||
|
|
||||||
|
// I AM NOT DONE
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let vec0 = Vec::new();
|
let vec0 = Vec::new();
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
// move_semantics2.rs
|
// move_semantics2.rs
|
||||||
// Make me compile without changing line 10! Scroll down for hints :)
|
// Make me compile without changing line 10! Scroll down for hints :)
|
||||||
|
|
||||||
|
// I AM NOT DONE
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let vec0 = Vec::new();
|
let vec0 = Vec::new();
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,8 @@
|
||||||
// (no lines with multiple semicolons necessary!)
|
// (no lines with multiple semicolons necessary!)
|
||||||
// Scroll down for hints :)
|
// Scroll down for hints :)
|
||||||
|
|
||||||
|
// I AM NOT DONE
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let vec0 = Vec::new();
|
let vec0 = Vec::new();
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,8 @@
|
||||||
// in `fn main`, we instead create it within `fn fill_vec` and transfer the
|
// 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!
|
// freshly created vector from fill_vec to its caller. Scroll for hints!
|
||||||
|
|
||||||
|
// I AM NOT DONE
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let vec0 = Vec::new();
|
let vec0 = Vec::new();
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,8 @@
|
||||||
// Fill in the rest of the line that has code missing!
|
// Fill in the rest of the line that has code missing!
|
||||||
// No hints, there's no tricks, just get used to typing these :)
|
// No hints, there's no tricks, just get used to typing these :)
|
||||||
|
|
||||||
|
// I AM NOT DONE
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
// Booleans (`bool`)
|
// Booleans (`bool`)
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,8 @@
|
||||||
// Fill in the rest of the line that has code missing!
|
// Fill in the rest of the line that has code missing!
|
||||||
// No hints, there's no tricks, just get used to typing these :)
|
// No hints, there's no tricks, just get used to typing these :)
|
||||||
|
|
||||||
|
// I AM NOT DONE
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
// Characters (`char`)
|
// Characters (`char`)
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,8 @@
|
||||||
// Create an array with at least 100 elements in it where the ??? is.
|
// Create an array with at least 100 elements in it where the ??? is.
|
||||||
// Scroll down for hints!
|
// Scroll down for hints!
|
||||||
|
|
||||||
|
// I AM NOT DONE
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let a = ???
|
let a = ???
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,8 @@
|
||||||
// Get a slice out of Array a where the ??? is so that the `if` statement
|
// Get a slice out of Array a where the ??? is so that the `if` statement
|
||||||
// returns true. Scroll down for hints!!
|
// returns true. Scroll down for hints!!
|
||||||
|
|
||||||
|
// I AM NOT DONE
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn main() {
|
fn main() {
|
||||||
let a = [1, 2, 3, 4, 5];
|
let a = [1, 2, 3, 4, 5];
|
||||||
|
|
|
@ -2,6 +2,8 @@
|
||||||
// Destructure the `cat` tuple so that the println will work.
|
// Destructure the `cat` tuple so that the println will work.
|
||||||
// Scroll down for hints!
|
// Scroll down for hints!
|
||||||
|
|
||||||
|
// I AM NOT DONE
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let cat = ("Furry McFurson", 3.5);
|
let cat = ("Furry McFurson", 3.5);
|
||||||
let /* your pattern here */ = cat;
|
let /* your pattern here */ = cat;
|
||||||
|
|
|
@ -3,6 +3,8 @@
|
||||||
// You can put this right into the `println!` where the ??? is.
|
// You can put this right into the `println!` where the ??? is.
|
||||||
// Scroll down for hints!
|
// Scroll down for hints!
|
||||||
|
|
||||||
|
// I AM NOT DONE
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let numbers = (1, 2, 3);
|
let numbers = (1, 2, 3);
|
||||||
println!("The second number is {}", ???);
|
println!("The second number is {}", ???);
|
||||||
|
|
|
@ -4,6 +4,8 @@
|
||||||
// somewhere. Try not to create any copies of the `numbers` Vec!
|
// somewhere. Try not to create any copies of the `numbers` Vec!
|
||||||
// Scroll down for hints :)
|
// Scroll down for hints :)
|
||||||
|
|
||||||
|
// I AM NOT DONE
|
||||||
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::thread;
|
use std::thread;
|
||||||
|
|
||||||
|
|
|
@ -5,6 +5,8 @@
|
||||||
// Step 3. Apply the `capitalize_first` function again to a list, but try and ensure it returns a single string
|
// 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!
|
// As always, there are hints below!
|
||||||
|
|
||||||
|
// I AM NOT DONE
|
||||||
|
|
||||||
pub fn capitalize_first(input: &str) -> String {
|
pub fn capitalize_first(input: &str) -> String {
|
||||||
let mut c = input.chars();
|
let mut c = input.chars();
|
||||||
match c.next() {
|
match c.next() {
|
||||||
|
|
|
@ -8,6 +8,8 @@
|
||||||
// a major hint.
|
// a major hint.
|
||||||
// Have fun :-)
|
// Have fun :-)
|
||||||
|
|
||||||
|
// I AM NOT DONE
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq)]
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
pub enum DivisionError {
|
pub enum DivisionError {
|
||||||
NotDivisible(NotDivisibleError),
|
NotDivisible(NotDivisibleError),
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
// iterators4.rs
|
// iterators4.rs
|
||||||
|
|
||||||
|
// I AM NOT DONE
|
||||||
|
|
||||||
pub fn factorial(num: u64) -> u64 {
|
pub fn factorial(num: u64) -> u64 {
|
||||||
// Complete this function to return factorial of num
|
// Complete this function to return factorial of num
|
||||||
// Do not use:
|
// Do not use:
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
// strings1.rs
|
// strings1.rs
|
||||||
// Make me compile without changing the function signature! Scroll down for hints :)
|
// Make me compile without changing the function signature! Scroll down for hints :)
|
||||||
|
|
||||||
|
// I AM NOT DONE
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let answer = current_favorite_color();
|
let answer = current_favorite_color();
|
||||||
println!("My current favorite color is {}", answer);
|
println!("My current favorite color is {}", answer);
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
// strings2.rs
|
// strings2.rs
|
||||||
// Make me compile without changing the function signature! Scroll down for hints :)
|
// Make me compile without changing the function signature! Scroll down for hints :)
|
||||||
|
|
||||||
|
// I AM NOT DONE
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let word = String::from("green"); // Try not changing this line :)
|
let word = String::from("green"); // Try not changing this line :)
|
||||||
if is_a_color_word(word) {
|
if is_a_color_word(word) {
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
// structs1.rs
|
// structs1.rs
|
||||||
// Address all the TODOs to make the tests pass!
|
// Address all the TODOs to make the tests pass!
|
||||||
|
|
||||||
|
// I AM NOT DONE
|
||||||
|
|
||||||
struct ColorClassicStruct {
|
struct ColorClassicStruct {
|
||||||
// TODO: Something goes here
|
// TODO: Something goes here
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,8 @@
|
||||||
// Address all the TODOs to make the tests pass!
|
// Address all the TODOs to make the tests pass!
|
||||||
// No hints, just do it!
|
// No hints, just do it!
|
||||||
|
|
||||||
|
// I AM NOT DONE
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
struct Order {
|
struct Order {
|
||||||
name: String,
|
name: String,
|
||||||
|
|
|
@ -7,6 +7,8 @@
|
||||||
// more than 40 at once, each apple only costs 1! Write a function that calculates
|
// 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!
|
// the price of an order of apples given the order amount. No hints this time!
|
||||||
|
|
||||||
|
// I AM NOT DONE
|
||||||
|
|
||||||
// Put your function here!
|
// Put your function here!
|
||||||
// fn ..... {
|
// fn ..... {
|
||||||
|
|
||||||
|
|
|
@ -7,6 +7,8 @@
|
||||||
// you think each value is. That is, add either `string_slice` or `string`
|
// 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!
|
// before the parentheses on each line. If you're right, it will compile!
|
||||||
|
|
||||||
|
// I AM NOT DONE
|
||||||
|
|
||||||
fn string_slice(arg: &str) {
|
fn string_slice(arg: &str) {
|
||||||
println!("{}", arg);
|
println!("{}", arg);
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,6 +7,8 @@
|
||||||
// we expect to get when we call `times_two` with a negative number.
|
// we expect to get when we call `times_two` with a negative number.
|
||||||
// No hints, you can do this :)
|
// No hints, you can do this :)
|
||||||
|
|
||||||
|
// I AM NOT DONE
|
||||||
|
|
||||||
pub fn times_two(num: i32) -> i32 {
|
pub fn times_two(num: i32) -> i32 {
|
||||||
num * 2
|
num * 2
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,6 +5,8 @@
|
||||||
|
|
||||||
// Write a macro that passes the test! No hints this time, you can do it!
|
// Write a macro that passes the test! No hints this time, you can do it!
|
||||||
|
|
||||||
|
// I AM NOT DONE
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
if my_macro!("world!") != "Hello world!" {
|
if my_macro!("world!") != "Hello world!" {
|
||||||
panic!("Oh no! Wrong output!");
|
panic!("Oh no! Wrong output!");
|
||||||
|
|
|
@ -6,6 +6,8 @@
|
||||||
// This test has a problem with it -- make the test compile! Make the test
|
// This test has a problem with it -- make the test compile! Make the test
|
||||||
// pass! Make the test fail! Scroll down for hints :)
|
// pass! Make the test fail! Scroll down for hints :)
|
||||||
|
|
||||||
|
// I AM NOT DONE
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
|
|
|
@ -2,6 +2,8 @@
|
||||||
// This test has a problem with it -- make the test compile! Make the test
|
// This test has a problem with it -- make the test compile! Make the test
|
||||||
// pass! Make the test fail! Scroll down for hints :)
|
// pass! Make the test fail! Scroll down for hints :)
|
||||||
|
|
||||||
|
// I AM NOT DONE
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
|
|
|
@ -3,6 +3,8 @@
|
||||||
// the test passes. Then write a second test that tests whether we get the result
|
// 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!
|
// 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 {
|
pub fn is_even(num: i32) -> bool {
|
||||||
num % 2 == 0
|
num % 2 == 0
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,6 +5,8 @@
|
||||||
// of "waiting..." and the program ends without timing out the playground,
|
// of "waiting..." and the program ends without timing out the playground,
|
||||||
// you've got it :)
|
// you've got it :)
|
||||||
|
|
||||||
|
// I AM NOT DONE
|
||||||
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::thread;
|
use std::thread;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
|
@ -1,6 +1,13 @@
|
||||||
// variables1.rs
|
// variables1.rs
|
||||||
// Make me compile! Scroll down for hints :)
|
// 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() {
|
fn main() {
|
||||||
x = 5;
|
x = 5;
|
||||||
println!("x has the value {}", x);
|
println!("x has the value {}", x);
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
// variables2.rs
|
// variables2.rs
|
||||||
// Make me compile! Scroll down for hints :)
|
// Make me compile! Scroll down for hints :)
|
||||||
|
|
||||||
|
// I AM NOT DONE
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let x;
|
let x;
|
||||||
if x == 10 {
|
if x == 10 {
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
// variables3.rs
|
// variables3.rs
|
||||||
// Make me compile! Scroll down for hints :)
|
// Make me compile! Scroll down for hints :)
|
||||||
|
|
||||||
|
// I AM NOT DONE
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let x = 3;
|
let x = 3;
|
||||||
println!("Number {}", x);
|
println!("Number {}", x);
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
// variables4.rs
|
// variables4.rs
|
||||||
// Make me compile! Scroll down for hints :)
|
// Make me compile! Scroll down for hints :)
|
||||||
|
|
||||||
|
// I AM NOT DONE
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let x: i32;
|
let x: i32;
|
||||||
println!("Number {}", x);
|
println!("Number {}", x);
|
||||||
|
|
113
src/exercise.rs
113
src/exercise.rs
|
@ -1,16 +1,20 @@
|
||||||
|
use regex::Regex;
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use std::fmt::{self, Display, Formatter};
|
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::path::PathBuf;
|
||||||
use std::process::{self, Command, Output};
|
use std::process::{self, Command, Output};
|
||||||
|
|
||||||
const RUSTC_COLOR_ARGS: &[&str] = &["--color", "always"];
|
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 {
|
fn temp_file() -> String {
|
||||||
format!("./temp_{}", process::id())
|
format!("./temp_{}", process::id())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize, Copy, Clone)]
|
||||||
#[serde(rename_all = "lowercase")]
|
#[serde(rename_all = "lowercase")]
|
||||||
pub enum Mode {
|
pub enum Mode {
|
||||||
Compile,
|
Compile,
|
||||||
|
@ -28,6 +32,19 @@ pub struct Exercise {
|
||||||
pub mode: Mode,
|
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 {
|
impl Exercise {
|
||||||
pub fn compile(&self) -> Output {
|
pub fn compile(&self) -> Output {
|
||||||
match self.mode {
|
match self.mode {
|
||||||
|
@ -52,6 +69,48 @@ impl Exercise {
|
||||||
pub fn clean(&self) {
|
pub fn clean(&self) {
|
||||||
let _ignored = remove_file(&temp_file());
|
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 {
|
impl Display for Exercise {
|
||||||
|
@ -63,7 +122,6 @@ impl Display for Exercise {
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use super::*;
|
use super::*;
|
||||||
use std::fs::File;
|
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -76,4 +134,53 @@ mod test {
|
||||||
exercise.clean();
|
exercise.clean();
|
||||||
assert!(!Path::new(&temp_file()).exists());
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -110,11 +110,11 @@ fn watch(exercises: &[Exercise]) -> notify::Result<()> {
|
||||||
DebouncedEvent::Create(b) | DebouncedEvent::Chmod(b) | DebouncedEvent::Write(b) => {
|
DebouncedEvent::Create(b) | DebouncedEvent::Chmod(b) | DebouncedEvent::Write(b) => {
|
||||||
if b.extension() == Some(OsStr::new("rs")) && b.exists() {
|
if b.extension() == Some(OsStr::new("rs")) && b.exists() {
|
||||||
let filepath = b.as_path().canonicalize().unwrap();
|
let filepath = b.as_path().canonicalize().unwrap();
|
||||||
let exercise = exercises
|
let pending_exercises = exercises
|
||||||
.iter()
|
.iter()
|
||||||
.skip_while(|e| !filepath.ends_with(&e.path));
|
.skip_while(|e| !filepath.ends_with(&e.path));
|
||||||
clear_screen();
|
clear_screen();
|
||||||
let _ignored = verify(exercise);
|
let _ignored = verify(pending_exercises);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
|
|
|
@ -5,7 +5,9 @@ use indicatif::ProgressBar;
|
||||||
|
|
||||||
pub fn run(exercise: &Exercise) -> Result<(), ()> {
|
pub fn run(exercise: &Exercise) -> Result<(), ()> {
|
||||||
match exercise.mode {
|
match exercise.mode {
|
||||||
Mode::Test => test(exercise)?,
|
Mode::Test => {
|
||||||
|
test(exercise)?;
|
||||||
|
}
|
||||||
Mode::Compile => compile_and_run(exercise)?,
|
Mode::Compile => compile_and_run(exercise)?,
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
|
@ -1,18 +1,21 @@
|
||||||
use crate::exercise::{Exercise, Mode};
|
use crate::exercise::{ContextLine, Exercise, Mode, State};
|
||||||
use console::{style, Emoji};
|
use console::{style, Emoji};
|
||||||
use indicatif::ProgressBar;
|
use indicatif::ProgressBar;
|
||||||
|
|
||||||
pub fn verify<'a>(start_at: impl IntoIterator<Item = &'a Exercise>) -> Result<(), ()> {
|
pub fn verify<'a>(start_at: impl IntoIterator<Item = &'a Exercise>) -> Result<(), ()> {
|
||||||
for exercise in start_at {
|
for exercise in start_at {
|
||||||
match exercise.mode {
|
let is_done = match exercise.mode {
|
||||||
Mode::Test => test(&exercise)?,
|
Mode::Test => test(&exercise)?,
|
||||||
Mode::Compile => compile_only(&exercise)?,
|
Mode::Compile => compile_only(&exercise)?,
|
||||||
|
};
|
||||||
|
if !is_done {
|
||||||
|
return Err(());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn compile_only(exercise: &Exercise) -> Result<(), ()> {
|
fn compile_only(exercise: &Exercise) -> Result<bool, ()> {
|
||||||
let progress_bar = ProgressBar::new_spinner();
|
let progress_bar = ProgressBar::new_spinner();
|
||||||
progress_bar.set_message(format!("Compiling {}...", exercise).as_str());
|
progress_bar.set_message(format!("Compiling {}...", exercise).as_str());
|
||||||
progress_bar.enable_steady_tick(100);
|
progress_bar.enable_steady_tick(100);
|
||||||
|
@ -22,7 +25,12 @@ fn compile_only(exercise: &Exercise) -> Result<(), ()> {
|
||||||
let formatstr = format!("{} Successfully compiled {}!", Emoji("✅", "✓"), exercise);
|
let formatstr = format!("{} Successfully compiled {}!", Emoji("✅", "✓"), exercise);
|
||||||
println!("{}", style(formatstr).green());
|
println!("{}", style(formatstr).green());
|
||||||
exercise.clean();
|
exercise.clean();
|
||||||
Ok(())
|
if let State::Pending(context) = exercise.state() {
|
||||||
|
print_everything_looks_good(exercise.mode, context);
|
||||||
|
Ok(false)
|
||||||
|
} else {
|
||||||
|
Ok(true)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
let formatstr = format!(
|
let formatstr = format!(
|
||||||
"{} Compilation of {} failed! Compiler error message:\n",
|
"{} 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();
|
let progress_bar = ProgressBar::new_spinner();
|
||||||
progress_bar.set_message(format!("Testing {}...", exercise).as_str());
|
progress_bar.set_message(format!("Testing {}...", exercise).as_str());
|
||||||
progress_bar.enable_steady_tick(100);
|
progress_bar.enable_steady_tick(100);
|
||||||
|
@ -52,7 +60,12 @@ pub fn test(exercise: &Exercise) -> Result<(), ()> {
|
||||||
let formatstr = format!("{} Successfully tested {}!", Emoji("✅", "✓"), exercise);
|
let formatstr = format!("{} Successfully tested {}!", Emoji("✅", "✓"), exercise);
|
||||||
println!("{}", style(formatstr).green());
|
println!("{}", style(formatstr).green());
|
||||||
exercise.clean();
|
exercise.clean();
|
||||||
Ok(())
|
if let State::Pending(context) = exercise.state() {
|
||||||
|
print_everything_looks_good(exercise.mode, context);
|
||||||
|
Ok(false)
|
||||||
|
} else {
|
||||||
|
Ok(true)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
let formatstr = format!(
|
let formatstr = format!(
|
||||||
"{} Testing of {} failed! Please try again. Here's the output:",
|
"{} Testing of {} failed! Please try again. Here's the output:",
|
||||||
|
@ -77,3 +90,34 @@ pub fn test(exercise: &Exercise) -> Result<(), ()> {
|
||||||
Err(())
|
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
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
// fake_exercise
|
||||||
|
|
||||||
|
// I AM NOT DONE
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
|
||||||
|
}
|
|
@ -1,4 +1,7 @@
|
||||||
use assert_cmd::prelude::*;
|
use assert_cmd::prelude::*;
|
||||||
|
use glob::glob;
|
||||||
|
use std::fs::File;
|
||||||
|
use std::io::Read;
|
||||||
use std::process::Command;
|
use std::process::Command;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -105,3 +108,20 @@ fn run_single_test_no_exercise() {
|
||||||
.assert()
|
.assert()
|
||||||
.code(1);
|
.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
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue