mirror of
https://github.com/notohh/rustlings.git
synced 2024-11-21 21:42:23 -05:00
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.
This commit is contained in:
commit
88ec6f6b16
60 changed files with 309 additions and 12 deletions
8
Cargo.lock
generated
8
Cargo.lock
generated
|
@ -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
tests/fixture/state/finished_exercise.rs
Normal file
0
tests/fixture/state/finished_exercise.rs
Normal file
7
tests/fixture/state/pending_exercise.rs
Normal file
7
tests/fixture/state/pending_exercise.rs
Normal file
|
@ -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 a new issue