diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 00000000..89cf181d --- /dev/null +++ b/.editorconfig @@ -0,0 +1,7 @@ +root = true + +[*.rs] +end_of_line = lf +insert_final_newfile = true +indent_style = space +indent_size = 4 diff --git a/Cargo.lock b/Cargo.lock index c88baa6a..19572d56 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -204,6 +204,11 @@ name = "fuchsia-zircon-sys" 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" @@ -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 ab6ee81e..7cbe2a25 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 ee665db3..a2223d33 100644 --- a/exercises/enums/enums1.rs +++ b/exercises/enums/enums1.rs @@ -1,6 +1,8 @@ // enums1.rs // Make me compile! Execute `rustlings hint enums1` for hints! +// I AM NOT DONE + #[derive(Debug)] enum Message { // TODO: define a few types of messages as used below diff --git a/exercises/enums/enums2.rs b/exercises/enums/enums2.rs index f9eb8bd6..52ccb221 100644 --- a/exercises/enums/enums2.rs +++ b/exercises/enums/enums2.rs @@ -1,6 +1,8 @@ // enums2.rs // Make me compile! Execute `rustlings hint enums2` for hints! +// I AM NOT DONE + #[derive(Debug)] enum Message { // TODO: define the different variants used below diff --git a/exercises/enums/enums3.rs b/exercises/enums/enums3.rs index c5d81bf9..bb7dfb43 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 f585e290..9c24d85d 100644 --- a/exercises/error_handling/errors1.rs +++ b/exercises/error_handling/errors1.rs @@ -6,6 +6,8 @@ // this function to have. // Execute `rustlings hint errors1` for hints! +// I AM NOT DONE + pub fn generate_nametag_text(name: String) -> Option { 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 5ac63392..aad3a93f 100644 --- a/exercises/error_handling/errors2.rs +++ b/exercises/error_handling/errors2.rs @@ -16,6 +16,8 @@ // There are at least two ways to implement this that are both correct-- but // one is a lot shorter! Execute `rustlings hint errors2` for hints to both ways. +// I AM NOT DONE + use std::num::ParseIntError; pub fn total_cost(item_quantity: &str) -> Result { diff --git a/exercises/error_handling/errors3.rs b/exercises/error_handling/errors3.rs index d9ec1336..460ac5c4 100644 --- a/exercises/error_handling/errors3.rs +++ b/exercises/error_handling/errors3.rs @@ -4,6 +4,8 @@ // Why not? What should we do to fix it? // Execute `rustlings hint errors3` for hints! +// I AM NOT DONE + use std::num::ParseIntError; fn main() { diff --git a/exercises/error_handling/errorsn.rs b/exercises/error_handling/errorsn.rs index 2f3566b4..fc25308c 100644 --- a/exercises/error_handling/errorsn.rs +++ b/exercises/error_handling/errorsn.rs @@ -15,6 +15,8 @@ // // Execute `rustlings hint errors4` for hints :) +// I AM NOT DONE + use std::error; use std::fmt; use std::io; diff --git a/exercises/error_handling/option1.rs b/exercises/error_handling/option1.rs index e334e938..5d81b150 100644 --- a/exercises/error_handling/option1.rs +++ b/exercises/error_handling/option1.rs @@ -4,6 +4,8 @@ // on `None`. Handle this in a more graceful way than calling `unwrap`! // Execute `rustlings hint option1` for hints :) +// I AM NOT DONE + pub fn pop_too_much() -> bool { let mut list = vec![3]; diff --git a/exercises/error_handling/result1.rs b/exercises/error_handling/result1.rs index c3f2d6e3..352a6c25 100644 --- a/exercises/error_handling/result1.rs +++ b/exercises/error_handling/result1.rs @@ -1,6 +1,8 @@ // result1.rs // Make this test pass! Execute `rustlings hint option2` for hints :) +// I AM NOT DONE + #[derive(PartialEq, Debug)] struct PositiveNonzeroInteger(u64); diff --git a/exercises/functions/functions1.rs b/exercises/functions/functions1.rs index 313fe1eb..49d48e93 100644 --- a/exercises/functions/functions1.rs +++ b/exercises/functions/functions1.rs @@ -1,6 +1,8 @@ // functions1.rs // Make me compile! Execute `rustlings hint function1` for hints :) +// I AM NOT DONE + fn main() { call_me(); } diff --git a/exercises/functions/functions2.rs b/exercises/functions/functions2.rs index 9bb3d76e..108ba38b 100644 --- a/exercises/functions/functions2.rs +++ b/exercises/functions/functions2.rs @@ -1,6 +1,8 @@ // functions2.rs // Make me compile! Execute `rustlings hint functions2` for hints :) +// I AM NOT DONE + fn main() { call_me(3); } diff --git a/exercises/functions/functions3.rs b/exercises/functions/functions3.rs index b2e90f6d..e3c1bf73 100644 --- a/exercises/functions/functions3.rs +++ b/exercises/functions/functions3.rs @@ -1,6 +1,8 @@ // functions3.rs // Make me compile! Execute `rustlings hint functions3` for hints :) +// I AM NOT DONE + fn main() { call_me(); } diff --git a/exercises/functions/functions4.rs b/exercises/functions/functions4.rs index 78fc27b7..6bf46f08 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 c7841a6e..d22aa6c8 100644 --- a/exercises/functions/functions5.rs +++ b/exercises/functions/functions5.rs @@ -1,6 +1,8 @@ // functions5.rs // Make me compile! Execute `rustlings hint functions5` for hints :) +// I AM NOT DONE + fn main() { let answer = square(3); println!("The answer is {}", answer); diff --git a/exercises/if/if1.rs b/exercises/if/if1.rs index bce052af..90867545 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 0bc2a69e..ed0dac85 100644 --- a/exercises/macros/macros1.rs +++ b/exercises/macros/macros1.rs @@ -1,6 +1,8 @@ // macros1.rs // Make me compile! Execute `rustlings hint macros1` for hints :) +// I AM NOT DONE + macro_rules! my_macro { () => { println!("Check out my macro!"); diff --git a/exercises/macros/macros2.rs b/exercises/macros/macros2.rs index c4b45101..d0be1236 100644 --- a/exercises/macros/macros2.rs +++ b/exercises/macros/macros2.rs @@ -1,6 +1,8 @@ // macros2.rs // Make me compile! Execute `rustlings hint macros2` for hints :) +// I AM NOT DONE + fn main() { my_macro!(); } diff --git a/exercises/macros/macros3.rs b/exercises/macros/macros3.rs index 9b08adc0..93a43113 100644 --- a/exercises/macros/macros3.rs +++ b/exercises/macros/macros3.rs @@ -2,6 +2,8 @@ // Make me compile, without taking the macro out of the module! // Execute `rustlings hint macros3` for hints :) +// I AM NOT DONE + mod macros { macro_rules! my_macro { () => { diff --git a/exercises/macros/macros4.rs b/exercises/macros/macros4.rs index 2ecba7d0..3a748078 100644 --- a/exercises/macros/macros4.rs +++ b/exercises/macros/macros4.rs @@ -1,6 +1,8 @@ // macros4.rs // Make me compile! Execute `rustlings hint macros4` for hints :) +// I AM NOT DONE + macro_rules! my_macro { () => { println!("Check out my macro!"); diff --git a/exercises/modules/modules1.rs b/exercises/modules/modules1.rs index c50d62f0..812dfeef 100644 --- a/exercises/modules/modules1.rs +++ b/exercises/modules/modules1.rs @@ -1,6 +1,8 @@ // modules1.rs // Make me compile! Execute `rustlings hint modules1` for hints :) +// I AM NOT DONE + mod sausage_factory { fn make_sausage() { println!("sausage!"); diff --git a/exercises/modules/modules2.rs b/exercises/modules/modules2.rs index c3bf4f79..fde439d1 100644 --- a/exercises/modules/modules2.rs +++ b/exercises/modules/modules2.rs @@ -1,6 +1,8 @@ // modules2.rs // Make me compile! Execute `rustlings hint modules2` for hints :) +// I AM NOT DONE + mod delicious_snacks { use self::fruits::PEAR as fruit; use self::veggies::CUCUMBER as veggie; diff --git a/exercises/move_semantics/move_semantics1.rs b/exercises/move_semantics/move_semantics1.rs index a69ec600..e2f5876d 100644 --- a/exercises/move_semantics/move_semantics1.rs +++ b/exercises/move_semantics/move_semantics1.rs @@ -1,6 +1,8 @@ // move_semantics1.rs // Make me compile! Execute `rustlings hint move_semantics1` for hints :) +// I AM NOT DONE + fn main() { let vec0 = Vec::new(); diff --git a/exercises/move_semantics/move_semantics2.rs b/exercises/move_semantics/move_semantics2.rs index b9450422..9233bb7e 100644 --- a/exercises/move_semantics/move_semantics2.rs +++ b/exercises/move_semantics/move_semantics2.rs @@ -2,6 +2,8 @@ // Make me compile without changing line 10! // Execute `rustlings hint move_semantics2` for hints :) +// I AM NOT DONE + fn main() { let vec0 = Vec::new(); diff --git a/exercises/move_semantics/move_semantics3.rs b/exercises/move_semantics/move_semantics3.rs index 3f7958ff..43fef74f 100644 --- a/exercises/move_semantics/move_semantics3.rs +++ b/exercises/move_semantics/move_semantics3.rs @@ -3,6 +3,8 @@ // (no lines with multiple semicolons necessary!) // Execute `rustlings hint move_semantics3` for hints :) +// I AM NOT DONE + fn main() { let vec0 = Vec::new(); diff --git a/exercises/move_semantics/move_semantics4.rs b/exercises/move_semantics/move_semantics4.rs index 2143b818..a1c4a413 100644 --- a/exercises/move_semantics/move_semantics4.rs +++ b/exercises/move_semantics/move_semantics4.rs @@ -4,6 +4,8 @@ // freshly created vector from fill_vec to its caller. // Execute `rustlings hint move_semantics4` for hints! +// I AM NOT DONE + fn main() { let vec0 = Vec::new(); diff --git a/exercises/primitive_types/primitive_types1.rs b/exercises/primitive_types/primitive_types1.rs index c3d11fe8..09121392 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 f5c8f87a..6576a4d5 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 e58c76c5..dfd6351c 100644 --- a/exercises/primitive_types/primitive_types3.rs +++ b/exercises/primitive_types/primitive_types3.rs @@ -2,6 +2,8 @@ // Create an array with at least 100 elements in it where the ??? is. // Execute `rustlings hint primitive_types3` for hints! +// I AM NOT DONE + fn main() { let a = ??? diff --git a/exercises/primitive_types/primitive_types4.rs b/exercises/primitive_types/primitive_types4.rs index 2efa58a6..ff1e2792 100644 --- a/exercises/primitive_types/primitive_types4.rs +++ b/exercises/primitive_types/primitive_types4.rs @@ -2,6 +2,8 @@ // Get a slice out of Array a where the ??? is so that the `if` statement // returns true. Execute `rustlings hint primitive_types4` for hints!! +// I AM NOT DONE + #[test] fn main() { let a = [1, 2, 3, 4, 5]; diff --git a/exercises/primitive_types/primitive_types5.rs b/exercises/primitive_types/primitive_types5.rs index 6ab1c215..680d8d23 100644 --- a/exercises/primitive_types/primitive_types5.rs +++ b/exercises/primitive_types/primitive_types5.rs @@ -2,6 +2,8 @@ // Destructure the `cat` tuple so that the println will work. // Execute `rustlings hint primitive_types5` for hints! +// I AM NOT DONE + fn main() { let cat = ("Furry McFurson", 3.5); let /* your pattern here */ = cat; diff --git a/exercises/primitive_types/primitive_types6.rs b/exercises/primitive_types/primitive_types6.rs index 219a53ef..2bc817e9 100644 --- a/exercises/primitive_types/primitive_types6.rs +++ b/exercises/primitive_types/primitive_types6.rs @@ -3,6 +3,8 @@ // You can put this right into the `println!` where the ??? is. // Execute `rustlings hint primitive_types6` for hints! +// I AM NOT DONE + fn main() { let numbers = (1, 2, 3); println!("The second number is {}", ???); diff --git a/exercises/standard_library_types/arc1.rs b/exercises/standard_library_types/arc1.rs index 5aa02d9a..04c169f1 100644 --- a/exercises/standard_library_types/arc1.rs +++ b/exercises/standard_library_types/arc1.rs @@ -4,6 +4,8 @@ // somewhere. Try not to create any copies of the `numbers` Vec! // Execute `rustlings help arc1` for hints :) +// I AM NOT DONE + use std::sync::Arc; use std::thread; diff --git a/exercises/standard_library_types/iterators2.rs b/exercises/standard_library_types/iterators2.rs index e70d5b66..a1274a2d 100644 --- a/exercises/standard_library_types/iterators2.rs +++ b/exercises/standard_library_types/iterators2.rs @@ -5,6 +5,8 @@ // Step 3. Apply the `capitalize_first` function again to a list, but try and ensure it returns a single string // As always, there are hints if you execute `rustlings hint iterators2`! +// I AM NOT DONE + pub fn capitalize_first(input: &str) -> String { let mut c = input.chars(); match c.next() { diff --git a/exercises/standard_library_types/iterators3.rs b/exercises/standard_library_types/iterators3.rs index d5a5afaa..353cea62 100644 --- a/exercises/standard_library_types/iterators3.rs +++ b/exercises/standard_library_types/iterators3.rs @@ -7,6 +7,8 @@ // Execute `rustlings hint iterators3` to get some hints! // Have fun :-) +// I AM NOT DONE + #[derive(Debug, PartialEq, Eq)] pub enum DivisionError { NotDivisible(NotDivisibleError), diff --git a/exercises/standard_library_types/iterators4.rs b/exercises/standard_library_types/iterators4.rs index bcb7f3cb..b945613f 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 0faf86e6..80902444 100644 --- a/exercises/strings/strings1.rs +++ b/exercises/strings/strings1.rs @@ -2,6 +2,8 @@ // Make me compile without changing the function signature! // Execute `rustlings hint strings1` for hints ;) +// I AM NOT DONE + fn main() { let answer = current_favorite_color(); println!("My current favorite color is {}", answer); diff --git a/exercises/strings/strings2.rs b/exercises/strings/strings2.rs index bd0f35cc..5a2ce74a 100644 --- a/exercises/strings/strings2.rs +++ b/exercises/strings/strings2.rs @@ -2,6 +2,8 @@ // Make me compile without changing the function signature! // Execute `rustlings hint strings2` for hints :) +// I AM NOT DONE + fn main() { let word = String::from("green"); // Try not changing this line :) if is_a_color_word(word) { diff --git a/exercises/structs/structs1.rs b/exercises/structs/structs1.rs index 138b3fd6..6d0b2f49 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 db381e7b..0699137c 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 6c27355f..3e13ce55 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 7fe81c63..d01606c1 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 9a72118b..f94c36f4 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 e50f1b08..c543a648 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 5eb918a7..b37cefa3 100644 --- a/exercises/tests/tests1.rs +++ b/exercises/tests/tests1.rs @@ -6,6 +6,8 @@ // This test has a problem with it -- make the test compile! Make the test // pass! Make the test fail! Execute `rustlings hint tests1` for hints :) +// I AM NOT DONE + #[cfg(test)] mod tests { #[test] diff --git a/exercises/tests/tests2.rs b/exercises/tests/tests2.rs index 044ed9ec..0d981ad1 100644 --- a/exercises/tests/tests2.rs +++ b/exercises/tests/tests2.rs @@ -2,6 +2,8 @@ // This test has a problem with it -- make the test compile! Make the test // pass! Make the test fail! Execute `rustlings hint tests2` for hints :) +// I AM NOT DONE + #[cfg(test)] mod tests { #[test] diff --git a/exercises/tests/tests3.rs b/exercises/tests/tests3.rs index 0f244b3e..693b8aa5 100644 --- a/exercises/tests/tests3.rs +++ b/exercises/tests/tests3.rs @@ -4,6 +4,8 @@ // we expect to get when we call `is_even(5)`. // Execute `rustlings hint tests3` for hints :) +// I AM NOT DONE + pub fn is_even(num: i32) -> bool { num % 2 == 0 } diff --git a/exercises/threads/threads1.rs b/exercises/threads/threads1.rs index 33083213..288ddd14 100644 --- a/exercises/threads/threads1.rs +++ b/exercises/threads/threads1.rs @@ -5,6 +5,8 @@ // of "waiting..." and the program ends without timing out when running, // you've got it :) +// I AM NOT DONE + use std::sync::Arc; use std::thread; use std::time::Duration; diff --git a/exercises/variables/variables1.rs b/exercises/variables/variables1.rs index 65452c20..5ddc37d5 100644 --- a/exercises/variables/variables1.rs +++ b/exercises/variables/variables1.rs @@ -1,6 +1,13 @@ // variables1.rs // Make me compile! Execute the command `rustlings hint variables1` if you want a hint :) +// About this `I AM NOT DONE` thing: +// We sometimes encourage you to keep trying things on a given exercise, +// even after you already figured it out. If you got everything working and +// feel ready for the next exercise, you the `I AM NOT DONE` comment below. + +// I AM NOT DONE + fn main() { x = 5; println!("x has the value {}", x); diff --git a/exercises/variables/variables2.rs b/exercises/variables/variables2.rs index 388b05d6..7774a8fb 100644 --- a/exercises/variables/variables2.rs +++ b/exercises/variables/variables2.rs @@ -1,6 +1,8 @@ // variables2.rs // Make me compile! Execute the command `rustlings hint variables2` if you want a hint :) +// I AM NOT DONE + fn main() { let x; if x == 10 { diff --git a/exercises/variables/variables3.rs b/exercises/variables/variables3.rs index f2c9c635..07b1a521 100644 --- a/exercises/variables/variables3.rs +++ b/exercises/variables/variables3.rs @@ -1,6 +1,8 @@ // variables3.rs // Make me compile! Execute the command `rustlings hint variables3` if you want a hint :) +// I AM NOT DONE + fn main() { let x = 3; println!("Number {}", x); diff --git a/exercises/variables/variables4.rs b/exercises/variables/variables4.rs index f1ed6ef1..77f1e9ab 100644 --- a/exercises/variables/variables4.rs +++ b/exercises/variables/variables4.rs @@ -1,6 +1,8 @@ // variables4.rs // Make me compile! Execute the command `rustlings hint variables4` if you want a hint :) +// I AM NOT DONE + fn main() { let x: i32; println!("Number {}", x); diff --git a/src/exercise.rs b/src/exercise.rs index 0e8a1990..be59284f 100644 --- a/src/exercise.rs +++ b/src/exercise.rs @@ -1,16 +1,20 @@ +use regex::Regex; use serde::Deserialize; use std::fmt::{self, Display, Formatter}; -use std::fs::remove_file; +use std::fs::{remove_file, File}; +use std::io::Read; use std::path::PathBuf; use std::process::{self, Command, Output}; const RUSTC_COLOR_ARGS: &[&str] = &["--color", "always"]; +const I_AM_DONE_REGEX: &str = r"(?m)^\s*///?\s*I\s+AM\s+NOT\s+DONE"; +const CONTEXT: usize = 2; fn temp_file() -> String { format!("./temp_{}", process::id()) } -#[derive(Deserialize)] +#[derive(Deserialize, Copy, Clone)] #[serde(rename_all = "lowercase")] pub enum Mode { Compile, @@ -30,6 +34,19 @@ pub struct Exercise { pub hint: String, } +#[derive(PartialEq, Debug)] +pub enum State { + Done, + Pending(Vec), +} + +#[derive(PartialEq, Debug)] +pub struct ContextLine { + pub line: String, + pub number: usize, + pub important: bool, +} + impl Exercise { pub fn compile(&self) -> Output { match self.mode { @@ -54,6 +71,48 @@ impl Exercise { pub fn clean(&self) { let _ignored = remove_file(&temp_file()); } + + pub fn state(&self) -> State { + let mut source_file = + File::open(&self.path).expect("We were unable to open the exercise file!"); + + let source = { + let mut s = String::new(); + source_file + .read_to_string(&mut s) + .expect("We were unable to read the exercise file!"); + s + }; + + let re = Regex::new(I_AM_DONE_REGEX).unwrap(); + + if !re.is_match(&source) { + return State::Done; + } + + let matched_line_index = source + .lines() + .enumerate() + .filter_map(|(i, line)| if re.is_match(line) { Some(i) } else { None }) + .next() + .expect("This should not happen at all"); + + let min_line = ((matched_line_index as i32) - (CONTEXT as i32)).max(0) as usize; + let max_line = matched_line_index + CONTEXT; + + let context = source + .lines() + .enumerate() + .filter(|&(i, _)| i >= min_line && i <= max_line) + .map(|(i, line)| ContextLine { + line: line.to_string(), + number: i + 1, + important: i == matched_line_index, + }) + .collect(); + + State::Pending(context) + } } impl Display for Exercise { @@ -65,7 +124,6 @@ impl Display for Exercise { #[cfg(test)] mod test { use super::*; - use std::fs::File; use std::path::Path; #[test] @@ -80,4 +138,53 @@ mod test { exercise.clean(); assert!(!Path::new(&temp_file()).exists()); } + + #[test] + fn test_pending_state() { + let exercise = Exercise { + path: PathBuf::from("tests/fixture/state/pending_exercise.rs"), + mode: Mode::Compile, + }; + + let state = exercise.state(); + let expected = vec![ + ContextLine { + line: "// fake_exercise".to_string(), + number: 1, + important: false, + }, + ContextLine { + line: "".to_string(), + number: 2, + important: false, + }, + ContextLine { + line: "// I AM NOT DONE".to_string(), + number: 3, + important: true, + }, + ContextLine { + line: "".to_string(), + number: 4, + important: false, + }, + ContextLine { + line: "fn main() {".to_string(), + number: 5, + important: false, + }, + ]; + + assert_eq!(state, State::Pending(expected)); + } + + #[test] + fn test_finished_exercise() { + let exercise = Exercise { + path: PathBuf::from("tests/fixture/state/finished_exercise.rs"), + mode: Mode::Compile, + }; + + assert_eq!(exercise.state(), State::Done); + } } diff --git a/src/main.rs b/src/main.rs index 875f7675..5a4af539 100644 --- a/src/main.rs +++ b/src/main.rs @@ -127,11 +127,11 @@ fn watch(exercises: &[Exercise]) -> notify::Result<()> { DebouncedEvent::Create(b) | DebouncedEvent::Chmod(b) | DebouncedEvent::Write(b) => { if b.extension() == Some(OsStr::new("rs")) && b.exists() { let filepath = b.as_path().canonicalize().unwrap(); - let exercise = exercises + let pending_exercises = exercises .iter() .skip_while(|e| !filepath.ends_with(&e.path)); clear_screen(); - let _ignored = verify(exercise); + let _ignored = verify(pending_exercises); } } _ => {} diff --git a/src/run.rs b/src/run.rs index 1484351a..1f777ece 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 d066afaf..020102e5 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) -> 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 { 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 { 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) { + 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 00000000..e69de29b diff --git a/tests/fixture/state/pending_exercise.rs b/tests/fixture/state/pending_exercise.rs new file mode 100644 index 00000000..f579d0b4 --- /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 24268ec5..32f4341f 100644 --- a/tests/integration_tests.rs +++ b/tests/integration_tests.rs @@ -1,4 +1,7 @@ use assert_cmd::prelude::*; +use glob::glob; +use std::fs::File; +use std::io::Read; use std::process::Command; #[test] @@ -115,4 +118,20 @@ fn get_hint_for_single_test() { .assert() .code(0) .stdout("Hello!\n"); + +#[test] +fn all_exercises_require_confirmation() { + for exercise in glob("exercises/**/*.rs").unwrap() { + let path = exercise.unwrap(); + let source = { + let mut file = File::open(&path).unwrap(); + let mut s = String::new(); + file.read_to_string(&mut s).unwrap(); + s + }; + source.matches("// I AM NOT DONE").next().expect(&format!( + "There should be an `I AM NOT DONE` annotation in {:?}", + path + )); + } }