From fa1f239a702eb2c0b7e0115e986481156961bbc8 Mon Sep 17 00:00:00 2001 From: mo8it Date: Thu, 11 Apr 2024 02:51:02 +0200 Subject: Remove "I AM NOT DONE" and the verify mode and add AppState --- exercises/00_intro/intro1.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'exercises/00_intro/intro1.rs') diff --git a/exercises/00_intro/intro1.rs b/exercises/00_intro/intro1.rs index 5dd18b4..aa505a1 100644 --- a/exercises/00_intro/intro1.rs +++ b/exercises/00_intro/intro1.rs @@ -1,6 +1,6 @@ // intro1.rs // -// About this `I AM NOT DONE` thing: +// TODO: Update comment // 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, remove the `I AM NOT DONE` comment below. @@ -13,8 +13,6 @@ // Execute `rustlings hint intro1` or use the `hint` watch subcommand for a // hint. -// I AM NOT DONE - fn main() { println!("Hello and"); println!(r#" welcome to... "#); -- cgit v1.2.3 From 686143100fbb89e2a7ba4098134fe37bf0c69ad2 Mon Sep 17 00:00:00 2001 From: mo8it Date: Thu, 11 Apr 2024 02:55:58 +0200 Subject: Update intro1 --- exercises/00_intro/intro1.rs | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) (limited to 'exercises/00_intro/intro1.rs') diff --git a/exercises/00_intro/intro1.rs b/exercises/00_intro/intro1.rs index aa505a1..e4e0444 100644 --- a/exercises/00_intro/intro1.rs +++ b/exercises/00_intro/intro1.rs @@ -27,13 +27,6 @@ fn main() { println!("or logic error. The central concept behind Rustlings is to fix these errors and"); println!("solve the exercises. Good luck!"); println!(); - println!("The source for this exercise is in `exercises/00_intro/intro1.rs`. Have a look!"); - println!( - "Going forward, the source of the exercises will always be in the success/failure output." - ); - println!(); - println!( - "If you want to use rust-analyzer, Rust's LSP implementation, make sure your editor is set" - ); - println!("up, and then run `rustlings lsp` before continuing.") + println!("The file of this exercise is `exercises/00_intro/intro1.rs`. Have a look!"); + println!("The current exercise path is shown under the progress bar in the watch mode."); } -- cgit v1.2.3 From 5c0073a9485c4226e58b657cb49628919a28a942 Mon Sep 17 00:00:00 2001 From: mo8it Date: Sun, 14 Apr 2024 01:15:43 +0200 Subject: Tolerate changes in the state file --- Cargo.lock | 1 + Cargo.toml | 1 + exercises/00_intro/intro1.rs | 1 - info.toml | 272 +++++++++++++++++++++---------------------- src/app_state.rs | 207 +++++++++++++++----------------- src/app_state/state_file.rs | 112 ++++++++++++++++++ src/exercise.rs | 72 +++--------- src/info_file.rs | 81 +++++++++++++ src/init.rs | 23 ++-- src/list.rs | 11 +- src/list/state.rs | 35 +++--- src/main.rs | 40 ++++--- src/run.rs | 2 +- src/watch.rs | 15 ++- src/watch/debounce_event.rs | 44 ------- src/watch/notify_event.rs | 42 +++++++ 16 files changed, 552 insertions(+), 407 deletions(-) create mode 100644 src/app_state/state_file.rs create mode 100644 src/info_file.rs delete mode 100644 src/watch/debounce_event.rs create mode 100644 src/watch/notify_event.rs (limited to 'exercises/00_intro/intro1.rs') diff --git a/Cargo.lock b/Cargo.lock index 6c64661..dbf1923 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -684,6 +684,7 @@ dependencies = [ "assert_cmd", "clap", "crossterm", + "hashbrown", "notify-debouncer-mini", "predicates", "ratatui", diff --git a/Cargo.toml b/Cargo.toml index 285e7df..14ae9a1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -37,6 +37,7 @@ edition.workspace = true anyhow.workspace = true clap = { version = "4.5.4", features = ["derive"] } crossterm = "0.27.0" +hashbrown = "0.14.3" notify-debouncer-mini = "0.4.1" ratatui = "0.26.1" rustlings-macros = { path = "rustlings-macros" } diff --git a/exercises/00_intro/intro1.rs b/exercises/00_intro/intro1.rs index e4e0444..170d195 100644 --- a/exercises/00_intro/intro1.rs +++ b/exercises/00_intro/intro1.rs @@ -1,6 +1,5 @@ // intro1.rs // -// TODO: Update comment // 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, remove the `I AM NOT DONE` comment below. diff --git a/info.toml b/info.toml index b6b6800..fa90ad7 100644 --- a/info.toml +++ b/info.toml @@ -33,10 +33,11 @@ https://github.com/rust-lang/rustlings/blob/main/CONTRIBUTING.md # INTRO +# TODO: Update exercise [[exercises]] name = "intro1" -path = "exercises/00_intro/intro1.rs" -mode = "compile" +dir = "00_intro" +mode = "run" # TODO: Fix hint hint = """ Remove the `I AM NOT DONE` comment in the `exercises/intro00/intro1.rs` file @@ -44,8 +45,8 @@ to move on to the next exercise.""" [[exercises]] name = "intro2" -path = "exercises/00_intro/intro2.rs" -mode = "compile" +dir = "00_intro" +mode = "run" hint = """ The compiler is informing us that we've got the name of the print macro wrong, and has suggested an alternative.""" @@ -53,16 +54,16 @@ The compiler is informing us that we've got the name of the print macro wrong, a [[exercises]] name = "variables1" -path = "exercises/01_variables/variables1.rs" -mode = "compile" +dir = "01_variables" +mode = "run" hint = """ The declaration in the first line in the main function is missing a keyword that is needed in Rust to create a new variable binding.""" [[exercises]] name = "variables2" -path = "exercises/01_variables/variables2.rs" -mode = "compile" +dir = "01_variables" +mode = "run" hint = """ The compiler message is saying that Rust cannot infer the type that the variable binding `x` has with what is given here. @@ -80,8 +81,8 @@ What if `x` is the same type as `10`? What if it's a different type?""" [[exercises]] name = "variables3" -path = "exercises/01_variables/variables3.rs" -mode = "compile" +dir = "01_variables" +mode = "run" hint = """ Oops! In this exercise, we have a variable binding that we've created on in the first line in the `main` function, and we're trying to use it in the next line, @@ -94,8 +95,8 @@ programming language -- thankfully the Rust compiler has caught this for us!""" [[exercises]] name = "variables4" -path = "exercises/01_variables/variables4.rs" -mode = "compile" +dir = "01_variables" +mode = "run" hint = """ In Rust, variable bindings are immutable by default. But here we're trying to reassign a different value to `x`! There's a keyword we can use to make @@ -103,8 +104,8 @@ a variable binding mutable instead.""" [[exercises]] name = "variables5" -path = "exercises/01_variables/variables5.rs" -mode = "compile" +dir = "01_variables" +mode = "run" hint = """ In `variables4` we already learned how to make an immutable variable mutable using a special keyword. Unfortunately this doesn't help us much in this @@ -121,8 +122,8 @@ Try to solve this exercise afterwards using this technique.""" [[exercises]] name = "variables6" -path = "exercises/01_variables/variables6.rs" -mode = "compile" +dir = "01_variables" +mode = "run" hint = """ We know about variables and mutability, but there is another important type of variable available: constants. @@ -141,8 +142,8 @@ https://doc.rust-lang.org/book/ch03-01-variables-and-mutability.html#constants [[exercises]] name = "functions1" -path = "exercises/02_functions/functions1.rs" -mode = "compile" +dir = "02_functions" +mode = "run" hint = """ This main function is calling a function that it expects to exist, but the function doesn't exist. It expects this function to have the name `call_me`. @@ -151,24 +152,24 @@ Sounds a lot like `main`, doesn't it?""" [[exercises]] name = "functions2" -path = "exercises/02_functions/functions2.rs" -mode = "compile" +dir = "02_functions" +mode = "run" hint = """ Rust requires that all parts of a function's signature have type annotations, but `call_me` is missing the type annotation of `num`.""" [[exercises]] name = "functions3" -path = "exercises/02_functions/functions3.rs" -mode = "compile" +dir = "02_functions" +mode = "run" hint = """ This time, the function *declaration* is okay, but there's something wrong with the place where we're calling the function.""" [[exercises]] name = "functions4" -path = "exercises/02_functions/functions4.rs" -mode = "compile" +dir = "02_functions" +mode = "run" hint = """ The error message points to the function `sale_price` and says it expects a type after the `->`. This is where the function's return type should be -- take a @@ -179,8 +180,8 @@ for the inputs of the functions here, since the original prices shouldn't be neg [[exercises]] name = "functions5" -path = "exercises/02_functions/functions5.rs" -mode = "compile" +dir = "02_functions" +mode = "run" hint = """ This is a really common error that can be fixed by removing one character. It happens because Rust distinguishes between expressions and statements: @@ -198,7 +199,7 @@ They are not the same. There are two solutions: [[exercises]] name = "if1" -path = "exercises/03_if/if1.rs" +dir = "03_if" mode = "test" hint = """ It's possible to do this in one line if you would like! @@ -214,7 +215,7 @@ Remember in Rust that: [[exercises]] name = "if2" -path = "exercises/03_if/if2.rs" +dir = "03_if" mode = "test" hint = """ For that first compiler error, it's important in Rust that each conditional @@ -223,7 +224,7 @@ conditions checking different input values.""" [[exercises]] name = "if3" -path = "exercises/03_if/if3.rs" +dir = "03_if" mode = "test" hint = """ In Rust, every arm of an `if` expression has to return the same type of value. @@ -233,7 +234,6 @@ Make sure the type is consistent across all arms.""" [[exercises]] name = "quiz1" -path = "exercises/quiz1.rs" mode = "test" hint = "No hints this time ;)" @@ -241,20 +241,20 @@ hint = "No hints this time ;)" [[exercises]] name = "primitive_types1" -path = "exercises/04_primitive_types/primitive_types1.rs" -mode = "compile" +dir = "04_primitive_types" +mode = "run" hint = "No hints this time ;)" [[exercises]] name = "primitive_types2" -path = "exercises/04_primitive_types/primitive_types2.rs" -mode = "compile" +dir = "04_primitive_types" +mode = "run" hint = "No hints this time ;)" [[exercises]] name = "primitive_types3" -path = "exercises/04_primitive_types/primitive_types3.rs" -mode = "compile" +dir = "04_primitive_types" +mode = "run" hint = """ There's a shorthand to initialize Arrays with a certain size that does not require you to type in 100 items (but you certainly can if you want!). @@ -269,7 +269,7 @@ for `a.len() >= 100`?""" [[exercises]] name = "primitive_types4" -path = "exercises/04_primitive_types/primitive_types4.rs" +dir = "04_primitive_types" mode = "test" hint = """ Take a look at the 'Understanding Ownership -> Slices -> Other Slices' section @@ -284,8 +284,8 @@ https://doc.rust-lang.org/nomicon/coercions.html""" [[exercises]] name = "primitive_types5" -path = "exercises/04_primitive_types/primitive_types5.rs" -mode = "compile" +dir = "04_primitive_types" +mode = "run" hint = """ Take a look at the 'Data Types -> The Tuple Type' section of the book: https://doc.rust-lang.org/book/ch03-02-data-types.html#the-tuple-type @@ -297,7 +297,7 @@ of the tuple. You can do it!!""" [[exercises]] name = "primitive_types6" -path = "exercises/04_primitive_types/primitive_types6.rs" +dir = "04_primitive_types" mode = "test" hint = """ While you could use a destructuring `let` for the tuple here, try @@ -310,7 +310,7 @@ Now you have another tool in your toolbox!""" [[exercises]] name = "vecs1" -path = "exercises/05_vecs/vecs1.rs" +dir = "05_vecs" mode = "test" hint = """ In Rust, there are two ways to define a Vector. @@ -325,7 +325,7 @@ of the Rust book to learn more. [[exercises]] name = "vecs2" -path = "exercises/05_vecs/vecs2.rs" +dir = "05_vecs" mode = "test" hint = """ In the first function we are looping over the Vector and getting a reference to @@ -348,7 +348,7 @@ What do you think is the more commonly used pattern under Rust developers? [[exercises]] name = "move_semantics1" -path = "exercises/06_move_semantics/move_semantics1.rs" +dir = "06_move_semantics" mode = "test" hint = """ So you've got the "cannot borrow immutable local variable `vec` as mutable" @@ -362,7 +362,7 @@ happens!""" [[exercises]] name = "move_semantics2" -path = "exercises/06_move_semantics/move_semantics2.rs" +dir = "06_move_semantics" mode = "test" hint = """ When running this exercise for the first time, you'll notice an error about @@ -383,7 +383,7 @@ try them all: [[exercises]] name = "move_semantics3" -path = "exercises/06_move_semantics/move_semantics3.rs" +dir = "06_move_semantics" mode = "test" hint = """ The difference between this one and the previous ones is that the first line @@ -393,7 +393,7 @@ an existing binding to be a mutable binding instead of an immutable one :)""" [[exercises]] name = "move_semantics4" -path = "exercises/06_move_semantics/move_semantics4.rs" +dir = "06_move_semantics" mode = "test" hint = """ Stop reading whenever you feel like you have enough direction :) Or try @@ -407,7 +407,7 @@ So the end goal is to: [[exercises]] name = "move_semantics5" -path = "exercises/06_move_semantics/move_semantics5.rs" +dir = "06_move_semantics" mode = "test" hint = """ Carefully reason about the range in which each mutable reference is in @@ -419,8 +419,8 @@ https://doc.rust-lang.org/book/ch04-02-references-and-borrowing.html#mutable-ref [[exercises]] name = "move_semantics6" -path = "exercises/06_move_semantics/move_semantics6.rs" -mode = "compile" +dir = "06_move_semantics" +mode = "run" hint = """ To find the answer, you can consult the book section "References and Borrowing": https://doc.rust-lang.org/stable/book/ch04-02-references-and-borrowing.html @@ -440,7 +440,7 @@ Another hint: it has to do with the `&` character.""" [[exercises]] name = "structs1" -path = "exercises/07_structs/structs1.rs" +dir = "07_structs" mode = "test" hint = """ Rust has more than one type of struct. Three actually, all variants are used to @@ -460,7 +460,7 @@ https://doc.rust-lang.org/book/ch05-01-defining-structs.html""" [[exercises]] name = "structs2" -path = "exercises/07_structs/structs2.rs" +dir = "07_structs" mode = "test" hint = """ Creating instances of structs is easy, all you need to do is assign some values @@ -472,7 +472,7 @@ https://doc.rust-lang.org/stable/book/ch05-01-defining-structs.html#creating-ins [[exercises]] name = "structs3" -path = "exercises/07_structs/structs3.rs" +dir = "07_structs" mode = "test" hint = """ For `is_international`: What makes a package international? Seems related to @@ -488,21 +488,21 @@ https://doc.rust-lang.org/book/ch05-03-method-syntax.html""" [[exercises]] name = "enums1" -path = "exercises/08_enums/enums1.rs" -mode = "compile" +dir = "08_enums" +mode = "run" hint = "No hints this time ;)" [[exercises]] name = "enums2" -path = "exercises/08_enums/enums2.rs" -mode = "compile" +dir = "08_enums" +mode = "run" hint = """ You can create enumerations that have different variants with different types such as no data, anonymous structs, a single string, tuples, ...etc""" [[exercises]] name = "enums3" -path = "exercises/08_enums/enums3.rs" +dir = "08_enums" mode = "test" hint = """ As a first step, you can define enums to compile this code without errors. @@ -516,8 +516,8 @@ to get value in the variant.""" [[exercises]] name = "strings1" -path = "exercises/09_strings/strings1.rs" -mode = "compile" +dir = "09_strings" +mode = "run" hint = """ The `current_favorite_color` function is currently returning a string slice with the `'static` lifetime. We know this because the data of the string lives @@ -530,8 +530,8 @@ another way that uses the `From` trait.""" [[exercises]] name = "strings2" -path = "exercises/09_strings/strings2.rs" -mode = "compile" +dir = "09_strings" +mode = "run" hint = """ Yes, it would be really easy to fix this by just changing the value bound to `word` to be a string slice instead of a `String`, wouldn't it?? There is a way @@ -545,7 +545,7 @@ https://doc.rust-lang.org/stable/book/ch15-02-deref.html#implicit-deref-coercion [[exercises]] name = "strings3" -path = "exercises/09_strings/strings3.rs" +dir = "09_strings" mode = "test" hint = """ There's tons of useful standard library functions for strings. Let's try and use some of them: @@ -556,16 +556,16 @@ the string slice into an owned string, which you can then freely extend.""" [[exercises]] name = "strings4" -path = "exercises/09_strings/strings4.rs" -mode = "compile" +dir = "09_strings" +mode = "run" hint = "No hints this time ;)" # MODULES [[exercises]] name = "modules1" -path = "exercises/10_modules/modules1.rs" -mode = "compile" +dir = "10_modules" +mode = "run" hint = """ Everything is private in Rust by default-- but there's a keyword we can use to make something public! The compiler error should point to the thing that @@ -573,8 +573,8 @@ needs to be public.""" [[exercises]] name = "modules2" -path = "exercises/10_modules/modules2.rs" -mode = "compile" +dir = "10_modules" +mode = "run" hint = """ The delicious_snacks module is trying to present an external interface that is different than its internal structure (the `fruits` and `veggies` modules and @@ -585,8 +585,8 @@ Learn more at https://doc.rust-lang.org/book/ch07-04-bringing-paths-into-scope-w [[exercises]] name = "modules3" -path = "exercises/10_modules/modules3.rs" -mode = "compile" +dir = "10_modules" +mode = "run" hint = """ `UNIX_EPOCH` and `SystemTime` are declared in the `std::time` module. Add a `use` statement for these two to bring them into scope. You can use nested @@ -596,7 +596,7 @@ paths or the glob operator to bring these two in using only one line.""" [[exercises]] name = "hashmaps1" -path = "exercises/11_hashmaps/hashmaps1.rs" +dir = "11_hashmaps" mode = "test" hint = """ Hint 1: Take a look at the return type of the function to figure out @@ -608,7 +608,7 @@ Hint 2: Number of fruits should be at least 5. And you have to put [[exercises]] name = "hashmaps2" -path = "exercises/11_hashmaps/hashmaps2.rs" +dir = "11_hashmaps" mode = "test" hint = """ Use the `entry()` and `or_insert()` methods of `HashMap` to achieve this. @@ -617,7 +617,7 @@ Learn more at https://doc.rust-lang.org/stable/book/ch08-03-hash-maps.html#only- [[exercises]] name = "hashmaps3" -path = "exercises/11_hashmaps/hashmaps3.rs" +dir = "11_hashmaps" mode = "test" hint = """ Hint 1: Use the `entry()` and `or_insert()` methods of `HashMap` to insert @@ -635,7 +635,6 @@ Learn more at https://doc.rust-lang.org/book/ch08-03-hash-maps.html#updating-a-v [[exercises]] name = "quiz2" -path = "exercises/quiz2.rs" mode = "test" hint = "No hints this time ;)" @@ -643,7 +642,7 @@ hint = "No hints this time ;)" [[exercises]] name = "options1" -path = "exercises/12_options/options1.rs" +dir = "12_options" mode = "test" hint = """ Options can have a `Some` value, with an inner value, or a `None` value, @@ -655,7 +654,7 @@ it doesn't panic in your face later?""" [[exercises]] name = "options2" -path = "exercises/12_options/options2.rs" +dir = "12_options" mode = "test" hint = """ Check out: @@ -672,8 +671,8 @@ Also see `Option::flatten` [[exercises]] name = "options3" -path = "exercises/12_options/options3.rs" -mode = "compile" +dir = "12_options" +mode = "run" hint = """ The compiler says a partial move happened in the `match` statement. How can this be avoided? The compiler shows the correction needed. @@ -685,7 +684,7 @@ https://doc.rust-lang.org/std/keyword.ref.html""" [[exercises]] name = "errors1" -path = "exercises/13_error_handling/errors1.rs" +dir = "13_error_handling" mode = "test" hint = """ `Ok` and `Err` are the two variants of `Result`, so what the tests are saying @@ -701,7 +700,7 @@ To make this change, you'll need to: [[exercises]] name = "errors2" -path = "exercises/13_error_handling/errors2.rs" +dir = "13_error_handling" mode = "test" hint = """ One way to handle this is using a `match` statement on @@ -717,8 +716,8 @@ and give it a try!""" [[exercises]] name = "errors3" -path = "exercises/13_error_handling/errors3.rs" -mode = "compile" +dir = "13_error_handling" +mode = "run" hint = """ If other functions can return a `Result`, why shouldn't `main`? It's a fairly common convention to return something like `Result<(), ErrorType>` from your @@ -729,7 +728,7 @@ positive results.""" [[exercises]] name = "errors4" -path = "exercises/13_error_handling/errors4.rs" +dir = "13_error_handling" mode = "test" hint = """ `PositiveNonzeroInteger::new` is always creating a new instance and returning @@ -741,8 +740,8 @@ everything is... okay :)""" [[exercises]] name = "errors5" -path = "exercises/13_error_handling/errors5.rs" -mode = "compile" +dir = "13_error_handling" +mode = "run" hint = """ There are two different possible `Result` types produced within `main()`, which are propagated using `?` operators. How do we declare a return type from @@ -765,7 +764,7 @@ https://doc.rust-lang.org/stable/rust-by-example/error/multiple_error_types/reen [[exercises]] name = "errors6" -path = "exercises/13_error_handling/errors6.rs" +dir = "13_error_handling" mode = "test" hint = """ This exercise uses a completed version of `PositiveNonzeroInteger` from @@ -787,8 +786,8 @@ https://doc.rust-lang.org/std/result/enum.Result.html#method.map_err""" [[exercises]] name = "generics1" -path = "exercises/14_generics/generics1.rs" -mode = "compile" +dir = "14_generics" +mode = "run" hint = """ Vectors in Rust make use of generics to create dynamically sized arrays of any type. @@ -797,7 +796,7 @@ You need to tell the compiler what type we are pushing onto this vector.""" [[exercises]] name = "generics2" -path = "exercises/14_generics/generics2.rs" +dir = "14_generics" mode = "test" hint = """ Currently we are wrapping only values of type `u32`. @@ -811,7 +810,7 @@ If you are still stuck https://doc.rust-lang.org/stable/book/ch10-01-syntax.html [[exercises]] name = "traits1" -path = "exercises/15_traits/traits1.rs" +dir = "15_traits" mode = "test" hint = """ A discussion about Traits in Rust can be found at: @@ -820,7 +819,7 @@ https://doc.rust-lang.org/book/ch10-02-traits.html [[exercises]] name = "traits2" -path = "exercises/15_traits/traits2.rs" +dir = "15_traits" mode = "test" hint = """ Notice how the trait takes ownership of `self`, and returns `Self`. @@ -833,7 +832,7 @@ the documentation at: https://doc.rust-lang.org/std/vec/struct.Vec.html""" [[exercises]] name = "traits3" -path = "exercises/15_traits/traits3.rs" +dir = "15_traits" mode = "test" hint = """ Traits can have a default implementation for functions. Structs that implement @@ -845,7 +844,7 @@ See the documentation at: https://doc.rust-lang.org/book/ch10-02-traits.html#def [[exercises]] name = "traits4" -path = "exercises/15_traits/traits4.rs" +dir = "15_traits" mode = "test" hint = """ Instead of using concrete types as parameters you can use traits. Try replacing @@ -856,8 +855,8 @@ See the documentation at: https://doc.rust-lang.org/book/ch10-02-traits.html#tra [[exercises]] name = "traits5" -path = "exercises/15_traits/traits5.rs" -mode = "compile" +dir = "15_traits" +mode = "run" hint = """ To ensure a parameter implements multiple traits use the '+ syntax'. Try replacing the '??' with 'impl <> + <>'. @@ -869,7 +868,6 @@ See the documentation at: https://doc.rust-lang.org/book/ch10-02-traits.html#spe [[exercises]] name = "quiz3" -path = "exercises/quiz3.rs" mode = "test" hint = """ To find the best solution to this challenge you're going to need to think back @@ -881,16 +879,16 @@ You may also need this: `use std::fmt::Display;`.""" [[exercises]] name = "lifetimes1" -path = "exercises/16_lifetimes/lifetimes1.rs" -mode = "compile" +dir = "16_lifetimes" +mode = "run" hint = """ Let the compiler guide you. Also take a look at the book if you need help: https://doc.rust-lang.org/book/ch10-03-lifetime-syntax.html""" [[exercises]] name = "lifetimes2" -path = "exercises/16_lifetimes/lifetimes2.rs" -mode = "compile" +dir = "16_lifetimes" +mode = "run" hint = """ Remember that the generic lifetime `'a` will get the concrete lifetime that is equal to the smaller of the lifetimes of `x` and `y`. @@ -903,8 +901,8 @@ inner block: [[exercises]] name = "lifetimes3" -path = "exercises/16_lifetimes/lifetimes3.rs" -mode = "compile" +dir = "16_lifetimes" +mode = "run" hint = """ If you use a lifetime annotation in a struct's fields, where else does it need to be added?""" @@ -913,7 +911,7 @@ to be added?""" [[exercises]] name = "tests1" -path = "exercises/17_tests/tests1.rs" +dir = "17_tests" mode = "test" hint = """ You don't even need to write any code to test -- you can just test values and @@ -928,7 +926,7 @@ ones pass, and which ones fail :)""" [[exercises]] name = "tests2" -path = "exercises/17_tests/tests2.rs" +dir = "17_tests" mode = "test" hint = """ Like the previous exercise, you don't need to write any code to get this test @@ -941,7 +939,7 @@ argument comes first and which comes second!""" [[exercises]] name = "tests3" -path = "exercises/17_tests/tests3.rs" +dir = "17_tests" mode = "test" hint = """ You can call a function right where you're passing arguments to `assert!`. So @@ -952,7 +950,7 @@ what you're doing using `!`, like `assert!(!having_fun())`.""" [[exercises]] name = "tests4" -path = "exercises/17_tests/tests4.rs" +dir = "17_tests" mode = "test" hint = """ We expect method `Rectangle::new()` to panic for negative values. @@ -966,7 +964,7 @@ https://doc.rust-lang.org/stable/book/ch11-01-writing-tests.html#checking-for-pa [[exercises]] name = "iterators1" -path = "exercises/18_iterators/iterators1.rs" +dir = "18_iterators" mode = "test" hint = """ Step 1: @@ -989,7 +987,7 @@ https://doc.rust-lang.org/std/iter/trait.Iterator.html for some ideas. [[exercises]] name = "iterators2" -path = "exercises/18_iterators/iterators2.rs" +dir = "18_iterators" mode = "test" hint = """ Step 1: @@ -1015,7 +1013,7 @@ powerful and very general. Rust just needs to know the desired type.""" [[exercises]] name = "iterators3" -path = "exercises/18_iterators/iterators3.rs" +dir = "18_iterators" mode = "test" hint = """ The `divide` function needs to return the correct error when even division is @@ -1034,7 +1032,7 @@ powerful! It can make the solution to this exercise infinitely easier.""" [[exercises]] name = "iterators4" -path = "exercises/18_iterators/iterators4.rs" +dir = "18_iterators" mode = "test" hint = """ In an imperative language, you might write a `for` loop that updates a mutable @@ -1046,7 +1044,7 @@ Hint 2: Check out the `fold` and `rfold` methods!""" [[exercises]] name = "iterators5" -path = "exercises/18_iterators/iterators5.rs" +dir = "18_iterators" mode = "test" hint = """ The documentation for the `std::iter::Iterator` trait contains numerous methods @@ -1065,7 +1063,7 @@ a different method that could make your code more compact than using `fold`.""" [[exercises]] name = "box1" -path = "exercises/19_smart_pointers/box1.rs" +dir = "19_smart_pointers" mode = "test" hint = """ Step 1: @@ -1089,7 +1087,7 @@ definition and try other types! [[exercises]] name = "rc1" -path = "exercises/19_smart_pointers/rc1.rs" +dir = "19_smart_pointers" mode = "test" hint = """ This is a straightforward exercise to use the `Rc` type. Each `Planet` has @@ -1108,8 +1106,8 @@ See more at: https://doc.rust-lang.org/book/ch15-04-rc.html [[exercises]] name = "arc1" -path = "exercises/19_smart_pointers/arc1.rs" -mode = "compile" +dir = "19_smart_pointers" +mode = "run" hint = """ Make `shared_numbers` be an `Arc` from the numbers vector. Then, in order to avoid creating a copy of `numbers`, you'll need to create `child_numbers` @@ -1126,7 +1124,7 @@ https://doc.rust-lang.org/stable/book/ch16-00-concurrency.html [[exercises]] name = "cow1" -path = "exercises/19_smart_pointers/cow1.rs" +dir = "19_smart_pointers" mode = "test" hint = """ If `Cow` already owns the data it doesn't need to clone it when `to_mut()` is @@ -1140,8 +1138,8 @@ on the `Cow` type. [[exercises]] name = "threads1" -path = "exercises/20_threads/threads1.rs" -mode = "compile" +dir = "20_threads" +mode = "run" hint = """ `JoinHandle` is a struct that is returned from a spawned thread: https://doc.rust-lang.org/std/thread/fn.spawn.html @@ -1158,8 +1156,8 @@ https://doc.rust-lang.org/std/thread/struct.JoinHandle.html [[exercises]] name = "threads2" -path = "exercises/20_threads/threads2.rs" -mode = "compile" +dir = "20_threads" +mode = "run" hint = """ `Arc` is an Atomic Reference Counted pointer that allows safe, shared access to **immutable** data. But we want to *change* the number of `jobs_completed` @@ -1180,7 +1178,7 @@ https://doc.rust-lang.org/book/ch16-03-shared-state.html#sharing-a-mutext-betwee [[exercises]] name = "threads3" -path = "exercises/20_threads/threads3.rs" +dir = "20_threads" mode = "test" hint = """ An alternate way to handle concurrency between threads is to use an `mpsc` @@ -1199,8 +1197,8 @@ See https://doc.rust-lang.org/book/ch16-02-message-passing.html for more info. [[exercises]] name = "macros1" -path = "exercises/21_macros/macros1.rs" -mode = "compile" +dir = "21_macros" +mode = "run" hint = """ When you call a macro, you need to add something special compared to a regular function call. If you're stuck, take a look at what's inside @@ -1208,8 +1206,8 @@ regular function call. If you're stuck, take a look at what's inside [[exercises]] name = "macros2" -path = "exercises/21_macros/macros2.rs" -mode = "compile" +dir = "21_macros" +mode = "run" hint = """ Macros don't quite play by the same rules as the rest of Rust, in terms of what's available where. @@ -1219,8 +1217,8 @@ Unlike other things in Rust, the order of "where you define a macro" versus [[exercises]] name = "macros3" -path = "exercises/21_macros/macros3.rs" -mode = "compile" +dir = "21_macros" +mode = "run" hint = """ In order to use a macro outside of its module, you need to do something special to the module to lift the macro out into its parent. @@ -1230,8 +1228,8 @@ exported macros, if you've seen any of those around.""" [[exercises]] name = "macros4" -path = "exercises/21_macros/macros4.rs" -mode = "compile" +dir = "21_macros" +mode = "run" hint = """ You only need to add a single character to make this compile. @@ -1247,7 +1245,7 @@ https://veykril.github.io/tlborm/""" [[exercises]] name = "clippy1" -path = "exercises/22_clippy/clippy1.rs" +dir = "22_clippy" mode = "clippy" hint = """ Rust stores the highest precision version of any long or infinite precision @@ -1263,14 +1261,14 @@ appropriate replacement constant from `std::f32::consts`...""" [[exercises]] name = "clippy2" -path = "exercises/22_clippy/clippy2.rs" +dir = "22_clippy" mode = "clippy" hint = """ `for` loops over `Option` values are more clearly expressed as an `if let`""" [[exercises]] name = "clippy3" -path = "exercises/22_clippy/clippy3.rs" +dir = "22_clippy" mode = "clippy" hint = "No hints this time!" @@ -1278,7 +1276,7 @@ hint = "No hints this time!" [[exercises]] name = "using_as" -path = "exercises/23_conversions/using_as.rs" +dir = "23_conversions" mode = "test" hint = """ Use the `as` operator to cast one of the operands in the last line of the @@ -1286,14 +1284,14 @@ Use the `as` operator to cast one of the operands in the last line of the [[exercises]] name = "from_into" -path = "exercises/23_conversions/from_into.rs" +dir = "23_conversions" mode = "test" hint = """ Follow the steps provided right before the `From` implementation""" [[exercises]] name = "from_str" -path = "exercises/23_conversions/from_str.rs" +dir = "23_conversions" mode = "test" hint = """ The implementation of `FromStr` should return an `Ok` with a `Person` object, @@ -1314,7 +1312,7 @@ https://doc.rust-lang.org/stable/rust-by-example/error/multiple_error_types/reen [[exercises]] name = "try_from_into" -path = "exercises/23_conversions/try_from_into.rs" +dir = "23_conversions" mode = "test" hint = """ Follow the steps provided right before the `TryFrom` implementation. @@ -1337,7 +1335,7 @@ Challenge: Can you make the `TryFrom` implementations generic over many integer [[exercises]] name = "as_ref_mut" -path = "exercises/23_conversions/as_ref_mut.rs" +dir = "23_conversions" mode = "test" hint = """ Add `AsRef` or `AsMut` as a trait bound to the functions.""" diff --git a/src/app_state.rs b/src/app_state.rs index 2ea3db4..1a051b9 100644 --- a/src/app_state.rs +++ b/src/app_state.rs @@ -4,52 +4,16 @@ use crossterm::{ terminal::{Clear, ClearType}, ExecutableCommand, }; -use serde::{Deserialize, Serialize}; -use std::{ - fs, - io::{StdoutLock, Write}, -}; - -use crate::{exercise::Exercise, FENISH_LINE}; - -const BAD_INDEX_ERR: &str = "The current exercise index is higher than the number of exercises"; - -#[derive(Serialize, Deserialize)] -#[serde(deny_unknown_fields)] -struct StateFile { - current_exercise_ind: usize, - progress: Vec, -} - -impl StateFile { - fn read(exercises: &[Exercise]) -> Option { - let file_content = fs::read(".rustlings-state.json").ok()?; +use std::io::{StdoutLock, Write}; - let slf: Self = serde_json::de::from_slice(&file_content).ok()?; +mod state_file; - if slf.progress.len() != exercises.len() || slf.current_exercise_ind >= exercises.len() { - return None; - } - - Some(slf) - } - - fn read_or_default(exercises: &[Exercise]) -> Self { - Self::read(exercises).unwrap_or_else(|| Self { - current_exercise_ind: 0, - progress: vec![false; exercises.len()], - }) - } +use crate::{exercise::Exercise, info_file::InfoFile, FENISH_LINE}; - fn write(&self) -> Result<()> { - let mut buf = Vec::with_capacity(1024); - serde_json::ser::to_writer(&mut buf, self).context("Failed to serialize the state")?; - fs::write(".rustlings-state.json", buf) - .context("Failed to write the state file `.rustlings-state.json`")?; +use self::state_file::{write, StateFileDeser}; - Ok(()) - } -} +const STATE_FILE_NAME: &str = ".rustlings-state.json"; +const BAD_INDEX_ERR: &str = "The current exercise index is higher than the number of exercises"; #[must_use] pub enum ExercisesProgress { @@ -58,52 +22,85 @@ pub enum ExercisesProgress { } pub struct AppState { - state_file: StateFile, - exercises: &'static [Exercise], + current_exercise_ind: usize, + exercises: Vec, n_done: u16, - current_exercise: &'static Exercise, - final_message: &'static str, + welcome_message: String, + final_message: String, } impl AppState { - pub fn new(mut exercises: Vec, mut final_message: String) -> Self { - // Leaking especially for sending the exercises to the debounce event handler. - // Leaking is not a problem because the `AppState` instance lives until - // the end of the program. - exercises.shrink_to_fit(); - let exercises = exercises.leak(); - final_message.shrink_to_fit(); - let final_message = final_message.leak(); - - let state_file = StateFile::read_or_default(exercises); - let n_done = state_file - .progress - .iter() - .fold(0, |acc, done| acc + u16::from(*done)); - let current_exercise = &exercises[state_file.current_exercise_ind]; + pub fn new(info_file: InfoFile) -> Self { + let mut exercises = info_file + .exercises + .into_iter() + .map(|mut exercise_info| { + // Leaking to be able to borrow in the watch mode `Table`. + // Leaking is not a problem because the `AppState` instance lives until + // the end of the program. + let path = Box::leak(exercise_info.path().into_boxed_path()); + + exercise_info.name.shrink_to_fit(); + let name = exercise_info.name.leak(); + + let hint = exercise_info.hint.trim().to_owned(); + + Exercise { + name, + path, + mode: exercise_info.mode, + hint, + done: false, + } + }) + .collect::>(); + + let (current_exercise_ind, n_done) = StateFileDeser::read().map_or((0, 0), |state_file| { + let mut state_file_exercises = + hashbrown::HashMap::with_capacity(state_file.exercises.len()); + + for (ind, exercise_state) in state_file.exercises.into_iter().enumerate() { + state_file_exercises.insert( + exercise_state.name, + (ind == state_file.current_exercise_ind, exercise_state.done), + ); + } + + let mut current_exercise_ind = 0; + let mut n_done = 0; + for (ind, exercise) in exercises.iter_mut().enumerate() { + if let Some((current, done)) = state_file_exercises.get(exercise.name) { + if *done { + exercise.done = true; + n_done += 1; + } + + if *current { + current_exercise_ind = ind; + } + } + } + + (current_exercise_ind, n_done) + }); Self { - state_file, + current_exercise_ind, exercises, n_done, - current_exercise, - final_message, + welcome_message: info_file.welcome_message.unwrap_or_default(), + final_message: info_file.final_message.unwrap_or_default(), } } #[inline] pub fn current_exercise_ind(&self) -> usize { - self.state_file.current_exercise_ind - } - - #[inline] - pub fn progress(&self) -> &[bool] { - &self.state_file.progress + self.current_exercise_ind } #[inline] - pub fn exercises(&self) -> &'static [Exercise] { - self.exercises + pub fn exercises(&self) -> &[Exercise] { + &self.exercises } #[inline] @@ -112,8 +109,8 @@ impl AppState { } #[inline] - pub fn current_exercise(&self) -> &'static Exercise { - self.current_exercise + pub fn current_exercise(&self) -> &Exercise { + &self.exercises[self.current_exercise_ind] } pub fn set_current_exercise_ind(&mut self, ind: usize) -> Result<()> { @@ -121,70 +118,61 @@ impl AppState { bail!(BAD_INDEX_ERR); } - self.state_file.current_exercise_ind = ind; - self.current_exercise = &self.exercises[ind]; + self.current_exercise_ind = ind; - self.state_file.write() + write(self) } pub fn set_current_exercise_by_name(&mut self, name: &str) -> Result<()> { - let (ind, exercise) = self + // O(N) is fine since this method is used only once until the program exits. + // Building a hashmap would have more overhead. + self.current_exercise_ind = self .exercises .iter() - .enumerate() - .find(|(_, exercise)| exercise.name == name) + .position(|exercise| exercise.name == name) .with_context(|| format!("No exercise found for '{name}'!"))?; - self.state_file.current_exercise_ind = ind; - self.current_exercise = exercise; - - self.state_file.write() + write(self) } pub fn set_pending(&mut self, ind: usize) -> Result<()> { - let done = self - .state_file - .progress - .get_mut(ind) - .context(BAD_INDEX_ERR)?; - - if *done { - *done = false; + let exercise = self.exercises.get_mut(ind).context(BAD_INDEX_ERR)?; + + if exercise.done { + exercise.done = false; self.n_done -= 1; - self.state_file.write()?; + write(self)?; } Ok(()) } fn next_pending_exercise_ind(&self) -> Option { - let current_ind = self.state_file.current_exercise_ind; - - if current_ind == self.state_file.progress.len() - 1 { + if self.current_exercise_ind == self.exercises.len() - 1 { // The last exercise is done. // Search for exercises not done from the start. - return self.state_file.progress[..current_ind] + return self.exercises[..self.current_exercise_ind] .iter() - .position(|done| !done); + .position(|exercise| !exercise.done); } // The done exercise isn't the last one. // Search for a pending exercise after the current one and then from the start. - match self.state_file.progress[current_ind + 1..] + match self.exercises[self.current_exercise_ind + 1..] .iter() - .position(|done| !done) + .position(|exercise| !exercise.done) { - Some(ind) => Some(current_ind + 1 + ind), - None => self.state_file.progress[..current_ind] + Some(ind) => Some(self.current_exercise_ind + 1 + ind), + None => self.exercises[..self.current_exercise_ind] .iter() - .position(|done| !done), + .position(|exercise| !exercise.done), } } pub fn done_current_exercise(&mut self, writer: &mut StdoutLock) -> Result { - let done = &mut self.state_file.progress[self.state_file.current_exercise_ind]; - if !*done { - *done = true; + let exercise = &mut self.exercises[self.current_exercise_ind]; + if !exercise.done { + exercise.done = true; self.n_done += 1; } @@ -198,15 +186,14 @@ impl AppState { if !exercise.run()?.status.success() { writer.write_fmt(format_args!("{}\n\n", "FAILED".red()))?; - self.state_file.current_exercise_ind = exercise_ind; - self.current_exercise = exercise; + self.current_exercise_ind = exercise_ind; // No check if the exercise is done before setting it to pending // because no pending exercise was found. - self.state_file.progress[exercise_ind] = false; + self.exercises[exercise_ind].done = false; self.n_done -= 1; - self.state_file.write()?; + write(self)?; return Ok(ExercisesProgress::Pending); } diff --git a/src/app_state/state_file.rs b/src/app_state/state_file.rs new file mode 100644 index 0000000..364a1fa --- /dev/null +++ b/src/app_state/state_file.rs @@ -0,0 +1,112 @@ +use anyhow::{Context, Result}; +use serde::{Deserialize, Serialize}; +use std::fs; + +use crate::exercise::Exercise; + +use super::{AppState, STATE_FILE_NAME}; + +#[derive(Deserialize)] +pub struct ExerciseStateDeser { + pub name: String, + pub done: bool, +} + +#[derive(Serialize)] +struct ExerciseStateSer<'a> { + name: &'a str, + done: bool, +} + +struct ExercisesStateSerializer<'a>(&'a [Exercise]); + +impl<'a> Serialize for ExercisesStateSerializer<'a> { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + let iter = self.0.iter().map(|exercise| ExerciseStateSer { + name: exercise.name, + done: exercise.done, + }); + + serializer.collect_seq(iter) + } +} + +#[derive(Deserialize)] +pub struct StateFileDeser { + pub current_exercise_ind: usize, + pub exercises: Vec, +} + +#[derive(Serialize)] +struct StateFileSer<'a> { + current_exercise_ind: usize, + exercises: ExercisesStateSerializer<'a>, +} + +impl StateFileDeser { + pub fn read() -> Option { + let file_content = fs::read(STATE_FILE_NAME).ok()?; + serde_json::de::from_slice(&file_content).ok() + } +} + +pub fn write(app_state: &AppState) -> Result<()> { + let content = StateFileSer { + current_exercise_ind: app_state.current_exercise_ind, + exercises: ExercisesStateSerializer(&app_state.exercises), + }; + + let mut buf = Vec::with_capacity(1024); + serde_json::ser::to_writer(&mut buf, &content).context("Failed to serialize the state")?; + fs::write(STATE_FILE_NAME, buf) + .with_context(|| format!("Failed to write the state file `{STATE_FILE_NAME}`"))?; + + Ok(()) +} + +#[cfg(test)] +mod tests { + use std::path::Path; + + use crate::info_file::Mode; + + use super::*; + + #[test] + fn ser_deser_sync() { + let current_exercise_ind = 1; + let exercises = [ + Exercise { + name: "1", + path: Path::new("exercises/1.rs"), + mode: Mode::Run, + hint: String::new(), + done: true, + }, + Exercise { + name: "2", + path: Path::new("exercises/2.rs"), + mode: Mode::Test, + hint: String::new(), + done: false, + }, + ]; + + let ser = StateFileSer { + current_exercise_ind, + exercises: ExercisesStateSerializer(&exercises), + }; + let deser: StateFileDeser = + serde_json::de::from_slice(&serde_json::ser::to_vec(&ser).unwrap()).unwrap(); + + assert_eq!(deser.current_exercise_ind, current_exercise_ind); + assert!(deser + .exercises + .iter() + .zip(exercises) + .all(|(deser, ser)| deser.name == ser.name && deser.done == ser.done)); + } +} diff --git a/src/exercise.rs b/src/exercise.rs index 6aa3b82..c5ece5f 100644 --- a/src/exercise.rs +++ b/src/exercise.rs @@ -1,66 +1,25 @@ use anyhow::{Context, Result}; -use serde::Deserialize; use std::{ - fmt::{self, Debug, Display, Formatter}, - fs::{self}, - path::PathBuf, + fmt::{self, Display, Formatter}, + path::Path, process::{Command, Output}, }; -use crate::embedded::{WriteStrategy, EMBEDDED_FILES}; - -// The mode of the exercise. -#[derive(Deserialize, Copy, Clone)] -#[serde(rename_all = "lowercase")] -pub enum Mode { - // The exercise should be compiled as a binary - Compile, - // The exercise should be compiled as a test harness - Test, - // The exercise should be linted with clippy - Clippy, -} - -#[derive(Deserialize)] -#[serde(deny_unknown_fields)] -pub struct InfoFile { - // TODO - pub welcome_message: Option, - pub final_message: Option, - pub exercises: Vec, -} - -impl InfoFile { - pub fn parse() -> Result { - // Read a local `info.toml` if it exists. - // Mainly to let the tests work for now. - let slf: Self = if let Ok(file_content) = fs::read_to_string("info.toml") { - toml_edit::de::from_str(&file_content) - } else { - toml_edit::de::from_str(include_str!("../info.toml")) - } - .context("Failed to parse `info.toml`")?; - - if slf.exercises.is_empty() { - panic!("{NO_EXERCISES_ERR}"); - } - - Ok(slf) - } -} +use crate::{ + embedded::{WriteStrategy, EMBEDDED_FILES}, + info_file::Mode, +}; -// Deserialized from the `info.toml` file. -#[derive(Deserialize)] -#[serde(deny_unknown_fields)] pub struct Exercise { - // Name of the exercise - pub name: String, - // The path to the file containing the exercise's source code - pub path: PathBuf, + // Exercise's unique name + pub name: &'static str, + // Exercise's path + pub path: &'static Path, // The mode of the exercise pub mode: Mode, // The hint text associated with the exercise pub hint: String, + pub done: bool, } impl Exercise { @@ -79,7 +38,7 @@ impl Exercise { .arg("always") .arg("-q") .arg("--bin") - .arg(&self.name) + .arg(self.name) .args(args) .output() .context("Failed to run Cargo") @@ -87,7 +46,7 @@ impl Exercise { pub fn run(&self) -> Result { match self.mode { - Mode::Compile => self.cargo_cmd("run", &[]), + Mode::Run => self.cargo_cmd("run", &[]), Mode::Test => self.cargo_cmd("test", &["--", "--nocapture", "--format", "pretty"]), Mode::Clippy => self.cargo_cmd( "clippy", @@ -98,7 +57,7 @@ impl Exercise { pub fn reset(&self) -> Result<()> { EMBEDDED_FILES - .write_exercise_to_disk(&self.path, WriteStrategy::Overwrite) + .write_exercise_to_disk(self.path, WriteStrategy::Overwrite) .with_context(|| format!("Failed to reset the exercise {self}")) } } @@ -108,6 +67,3 @@ impl Display for Exercise { Display::fmt(&self.path.display(), f) } } - -const NO_EXERCISES_ERR: &str = "There are no exercises yet! -If you are developing third-party exercises, add at least one exercise before testing."; diff --git a/src/info_file.rs b/src/info_file.rs new file mode 100644 index 0000000..dc97b92 --- /dev/null +++ b/src/info_file.rs @@ -0,0 +1,81 @@ +use anyhow::{bail, Context, Error, Result}; +use serde::Deserialize; +use std::{fs, path::PathBuf}; + +// The mode of the exercise. +#[derive(Deserialize, Copy, Clone)] +#[serde(rename_all = "lowercase")] +pub enum Mode { + // The exercise should be compiled as a binary + Run, + // The exercise should be compiled as a test harness + Test, + // The exercise should be linted with clippy + Clippy, +} + +// Deserialized from the `info.toml` file. +#[derive(Deserialize)] +pub struct ExerciseInfo { + // Name of the exercise + pub name: String, + // The exercise's directory inside the `exercises` directory + pub dir: Option, + // The mode of the exercise + pub mode: Mode, + // The hint text associated with the exercise + pub hint: String, +} + +impl ExerciseInfo { + pub fn path(&self) -> PathBuf { + let path = if let Some(dir) = &self.dir { + format!("exercises/{dir}/{}.rs", self.name) + } else { + format!("exercises/{}.rs", self.name) + }; + + PathBuf::from(path) + } +} + +#[derive(Deserialize)] +pub struct InfoFile { + pub welcome_message: Option, + pub final_message: Option, + pub exercises: Vec, +} + +impl InfoFile { + pub fn parse() -> Result { + // Read a local `info.toml` if it exists. + let slf: Self = match fs::read_to_string("info.toml") { + Ok(file_content) => toml_edit::de::from_str(&file_content) + .context("Failed to parse the `info.toml` file")?, + Err(e) => match e.kind() { + std::io::ErrorKind::NotFound => { + toml_edit::de::from_str(include_str!("../info.toml")) + .context("Failed to parse the embedded `info.toml` file")? + } + _ => return Err(Error::from(e).context("Failed to read the `info.toml` file")), + }, + }; + + if slf.exercises.is_empty() { + bail!("{NO_EXERCISES_ERR}"); + } + + let mut names_set = hashbrown::HashSet::with_capacity(slf.exercises.len()); + for exercise in &slf.exercises { + if !names_set.insert(exercise.name.as_str()) { + bail!("Exercise names must all be unique!") + } + } + drop(names_set); + + Ok(slf) + } +} + +const NO_EXERCISES_ERR: &str = "There are no exercises yet! +If you are developing third-party exercises, add at least one exercise before testing."; diff --git a/src/init.rs b/src/init.rs index 093610a..2badf37 100644 --- a/src/init.rs +++ b/src/init.rs @@ -6,17 +6,21 @@ use std::{ path::Path, }; -use crate::{embedded::EMBEDDED_FILES, exercise::Exercise}; +use crate::{embedded::EMBEDDED_FILES, info_file::ExerciseInfo}; -fn create_cargo_toml(exercises: &[Exercise]) -> io::Result<()> { +fn create_cargo_toml(exercise_infos: &[ExerciseInfo]) -> io::Result<()> { let mut cargo_toml = Vec::with_capacity(1 << 13); cargo_toml.extend_from_slice(b"bin = [\n"); - for exercise in exercises { + for exercise_info in exercise_infos { cargo_toml.extend_from_slice(b" { name = \""); - cargo_toml.extend_from_slice(exercise.name.as_bytes()); - cargo_toml.extend_from_slice(b"\", path = \""); - cargo_toml.extend_from_slice(exercise.path.to_str().unwrap().as_bytes()); - cargo_toml.extend_from_slice(b"\" },\n"); + cargo_toml.extend_from_slice(exercise_info.name.as_bytes()); + cargo_toml.extend_from_slice(b"\", path = \"exercises/"); + if let Some(dir) = &exercise_info.dir { + cargo_toml.extend_from_slice(dir.as_bytes()); + cargo_toml.extend_from_slice(b"/"); + } + cargo_toml.extend_from_slice(exercise_info.name.as_bytes()); + cargo_toml.extend_from_slice(b".rs\" },\n"); } cargo_toml.extend_from_slice( @@ -54,7 +58,7 @@ fn create_vscode_dir() -> Result<()> { Ok(()) } -pub fn init(exercises: &[Exercise]) -> Result<()> { +pub fn init(exercise_infos: &[ExerciseInfo]) -> Result<()> { if Path::new("exercises").is_dir() && Path::new("Cargo.toml").is_file() { bail!(PROBABLY_IN_RUSTLINGS_DIR_ERR); } @@ -74,7 +78,8 @@ pub fn init(exercises: &[Exercise]) -> Result<()> { .init_exercises_dir() .context("Failed to initialize the `rustlings/exercises` directory")?; - create_cargo_toml(exercises).context("Failed to create the file `rustlings/Cargo.toml`")?; + create_cargo_toml(exercise_infos) + .context("Failed to create the file `rustlings/Cargo.toml`")?; create_gitignore().context("Failed to create the file `rustlings/.gitignore`")?; diff --git a/src/list.rs b/src/list.rs index de120ea..2bb813d 100644 --- a/src/list.rs +++ b/src/list.rs @@ -5,7 +5,7 @@ use crossterm::{ ExecutableCommand, }; use ratatui::{backend::CrosstermBackend, Terminal}; -use std::{fmt::Write, io}; +use std::io; mod state; @@ -72,14 +72,7 @@ pub fn list(app_state: &mut AppState) -> Result<()> { ui_state.message.push_str(message); } KeyCode::Char('r') => { - let Some(exercise) = ui_state.reset_selected()? else { - continue; - }; - - ui_state = ui_state.with_updated_rows(); - ui_state - .message - .write_fmt(format_args!("The exercise {exercise} has been reset!"))?; + ui_state = ui_state.with_reset_selected()?; } KeyCode::Char('c') => { ui_state.selected_to_current_exercise()?; diff --git a/src/list/state.rs b/src/list/state.rs index 0dcfe88..38391a4 100644 --- a/src/list/state.rs +++ b/src/list/state.rs @@ -6,8 +6,9 @@ use ratatui::{ widgets::{Block, Borders, HighlightSpacing, Paragraph, Row, Table, TableState}, Frame, }; +use std::fmt::Write; -use crate::{app_state::AppState, exercise::Exercise, progress_bar::progress_bar_ratatui}; +use crate::{app_state::AppState, progress_bar::progress_bar_ratatui}; #[derive(Copy, Clone, PartialEq, Eq)] pub enum Filter { @@ -34,10 +35,9 @@ impl<'a> UiState<'a> { .app_state .exercises() .iter() - .zip(self.app_state.progress().iter().copied()) .enumerate() - .filter_map(|(ind, (exercise, done))| { - let exercise_state = if done { + .filter_map(|(ind, exercise)| { + let exercise_state = if exercise.done { if self.filter == Filter::Pending { return None; } @@ -62,7 +62,7 @@ impl<'a> UiState<'a> { Some(Row::new([ next, exercise_state, - Span::raw(&exercise.name), + Span::raw(exercise.name), Span::raw(exercise.path.to_string_lossy()), ])) }); @@ -212,29 +212,30 @@ impl<'a> UiState<'a> { Ok(()) } - pub fn reset_selected(&mut self) -> Result> { + pub fn with_reset_selected(mut self) -> Result { let Some(selected) = self.table_state.selected() else { - return Ok(None); + return Ok(self); }; let (ind, exercise) = self .app_state .exercises() .iter() - .zip(self.app_state.progress()) .enumerate() - .filter_map(|(ind, (exercise, done))| match self.filter { - Filter::Done => done.then_some((ind, exercise)), - Filter::Pending => (!done).then_some((ind, exercise)), + .filter_map(|(ind, exercise)| match self.filter { + Filter::Done => exercise.done.then_some((ind, exercise)), + Filter::Pending => (!exercise.done).then_some((ind, exercise)), Filter::None => Some((ind, exercise)), }) .nth(selected) .context("Invalid selection index")?; - self.app_state.set_pending(ind)?; exercise.reset()?; + self.message + .write_fmt(format_args!("The exercise {exercise} has been reset!"))?; + self.app_state.set_pending(ind)?; - Ok(Some(exercise)) + Ok(self.with_updated_rows()) } pub fn selected_to_current_exercise(&mut self) -> Result<()> { @@ -244,12 +245,12 @@ impl<'a> UiState<'a> { let ind = self .app_state - .progress() + .exercises() .iter() .enumerate() - .filter_map(|(ind, done)| match self.filter { - Filter::Done => done.then_some(ind), - Filter::Pending => (!done).then_some(ind), + .filter_map(|(ind, exercise)| match self.filter { + Filter::Done => exercise.done.then_some(ind), + Filter::Pending => (!exercise.done).then_some(ind), Filter::None => Some(ind), }) .nth(selected) diff --git a/src/main.rs b/src/main.rs index cdfa21f..a96e323 100644 --- a/src/main.rs +++ b/src/main.rs @@ -5,6 +5,7 @@ use std::{path::Path, process::exit}; mod app_state; mod embedded; mod exercise; +mod info_file; mod init; mod list; mod progress_bar; @@ -13,7 +14,7 @@ mod watch; use self::{ app_state::AppState, - exercise::InfoFile, + info_file::InfoFile, init::init, list::list, run::run, @@ -54,12 +55,10 @@ fn main() -> Result<()> { which::which("cargo").context(CARGO_NOT_FOUND_ERR)?; - let mut info_file = InfoFile::parse()?; - info_file.exercises.shrink_to_fit(); - let exercises = info_file.exercises; + let info_file = InfoFile::parse()?; if matches!(args.command, Some(Subcommands::Init)) { - init(&exercises).context("Initialization failed")?; + init(&info_file.exercises).context("Initialization failed")?; println!("{POST_INIT_MSG}"); return Ok(()); @@ -68,18 +67,29 @@ fn main() -> Result<()> { exit(1); } - let mut app_state = AppState::new(exercises, info_file.final_message.unwrap_or_default()); + let mut app_state = AppState::new(info_file); match args.command { - None => loop { - match watch(&mut app_state)? { - WatchExit::Shutdown => break, - // It is much easier to exit the watch mode, launch the list mode and then restart - // the watch mode instead of trying to pause the watch threads and correct the - // watch state. - WatchExit::List => list(&mut app_state)?, + None => { + // For the the notify event handler thread. + // Leaking is not a problem because the slice lives until the end of the program. + let exercise_paths = app_state + .exercises() + .iter() + .map(|exercise| exercise.path) + .collect::>() + .leak(); + + loop { + match watch(&mut app_state, exercise_paths)? { + WatchExit::Shutdown => break, + // It is much easier to exit the watch mode, launch the list mode and then restart + // the watch mode instead of trying to pause the watch threads and correct the + // watch state. + WatchExit::List => list(&mut app_state)?, + } } - }, + } // `Init` is handled above. Some(Subcommands::Init) => (), Some(Subcommands::Run { name }) => { @@ -90,10 +100,10 @@ fn main() -> Result<()> { } Some(Subcommands::Reset { name }) => { app_state.set_current_exercise_by_name(&name)?; - app_state.set_pending(app_state.current_exercise_ind())?; let exercise = app_state.current_exercise(); exercise.reset()?; println!("The exercise {exercise} has been reset!"); + app_state.set_pending(app_state.current_exercise_ind())?; } Some(Subcommands::Hint { name }) => { app_state.set_current_exercise_by_name(&name)?; diff --git a/src/run.rs b/src/run.rs index 4748549..9c504b5 100644 --- a/src/run.rs +++ b/src/run.rs @@ -17,7 +17,7 @@ pub fn run(app_state: &mut AppState) -> Result<()> { if !output.status.success() { app_state.set_pending(app_state.current_exercise_ind())?; - bail!("Ran {exercise} with errors"); + bail!("Ran {} with errors", app_state.current_exercise()); } stdout.write_fmt(format_args!( diff --git a/src/watch.rs b/src/watch.rs index beb69b3..58e829f 100644 --- a/src/watch.rs +++ b/src/watch.rs @@ -11,14 +11,14 @@ use std::{ time::Duration, }; -mod debounce_event; +mod notify_event; mod state; mod terminal_event; use crate::app_state::{AppState, ExercisesProgress}; use self::{ - debounce_event::DebounceEventHandler, + notify_event::DebounceEventHandler, state::WatchState, terminal_event::{terminal_event_handler, InputEvent}, }; @@ -40,13 +40,16 @@ pub enum WatchExit { List, } -pub fn watch(app_state: &mut AppState) -> Result { +pub fn watch( + app_state: &mut AppState, + exercise_paths: &'static [&'static Path], +) -> Result { let (tx, rx) = channel(); let mut debouncer = new_debouncer( Duration::from_secs(1), DebounceEventHandler { tx: tx.clone(), - exercises: app_state.exercises(), + exercise_paths, }, )?; debouncer @@ -85,10 +88,10 @@ pub fn watch(app_state: &mut AppState) -> Result { watch_state.render()?; } WatchEvent::NotifyErr(e) => { - return Err(Error::from(e).context("Exercise file watcher failed")) + return Err(Error::from(e).context("Exercise file watcher failed")); } WatchEvent::TerminalEventErr(e) => { - return Err(Error::from(e).context("Terminal event listener failed")) + return Err(Error::from(e).context("Terminal event listener failed")); } } } diff --git a/src/watch/debounce_event.rs b/src/watch/debounce_event.rs deleted file mode 100644 index 1dc92cb..0000000 --- a/src/watch/debounce_event.rs +++ /dev/null @@ -1,44 +0,0 @@ -use notify_debouncer_mini::{DebounceEventResult, DebouncedEventKind}; -use std::sync::mpsc::Sender; - -use crate::exercise::Exercise; - -use super::WatchEvent; - -pub struct DebounceEventHandler { - pub tx: Sender, - pub exercises: &'static [Exercise], -} - -impl notify_debouncer_mini::DebounceEventHandler for DebounceEventHandler { - fn handle_event(&mut self, event: DebounceEventResult) { - let event = match event { - Ok(event) => { - let Some(exercise_ind) = event - .iter() - .filter_map(|event| { - if event.kind != DebouncedEventKind::Any - || !event.path.extension().is_some_and(|ext| ext == "rs") - { - return None; - } - - self.exercises - .iter() - .position(|exercise| event.path.ends_with(&exercise.path)) - }) - .min() - else { - return; - }; - - WatchEvent::FileChange { exercise_ind } - } - Err(e) => WatchEvent::NotifyErr(e), - }; - - // An error occurs when the receiver is dropped. - // After dropping the receiver, the debouncer guard should also be dropped. - let _ = self.tx.send(event); - } -} diff --git a/src/watch/notify_event.rs b/src/watch/notify_event.rs new file mode 100644 index 0000000..0c8d669 --- /dev/null +++ b/src/watch/notify_event.rs @@ -0,0 +1,42 @@ +use notify_debouncer_mini::{DebounceEventResult, DebouncedEventKind}; +use std::{path::Path, sync::mpsc::Sender}; + +use super::WatchEvent; + +pub struct DebounceEventHandler { + pub tx: Sender, + pub exercise_paths: &'static [&'static Path], +} + +impl notify_debouncer_mini::DebounceEventHandler for DebounceEventHandler { + fn handle_event(&mut self, event: DebounceEventResult) { + let event = match event { + Ok(event) => { + let Some(exercise_ind) = event + .iter() + .filter_map(|event| { + if event.kind != DebouncedEventKind::Any + || !event.path.extension().is_some_and(|ext| ext == "rs") + { + return None; + } + + self.exercise_paths + .iter() + .position(|path| event.path.ends_with(path)) + }) + .min() + else { + return; + }; + + WatchEvent::FileChange { exercise_ind } + } + Err(e) => WatchEvent::NotifyErr(e), + }; + + // An error occurs when the receiver is dropped. + // After dropping the receiver, the debouncer guard should also be dropped. + let _ = self.tx.send(event); + } +} -- cgit v1.2.3 From 2f810a4da67233716ad93e00afff6e8b260f4807 Mon Sep 17 00:00:00 2001 From: mo8it Date: Wed, 17 Apr 2024 23:34:27 +0200 Subject: Clean up and unify exercises --- exercises/00_intro/intro1.rs | 5 -- exercises/00_intro/intro2.rs | 5 -- exercises/01_variables/variables1.rs | 5 -- exercises/01_variables/variables2.rs | 5 -- exercises/01_variables/variables3.rs | 5 -- exercises/01_variables/variables4.rs | 5 -- exercises/01_variables/variables5.rs | 5 -- exercises/01_variables/variables6.rs | 5 -- exercises/02_functions/functions1.rs | 5 -- exercises/02_functions/functions2.rs | 5 -- exercises/02_functions/functions3.rs | 5 -- exercises/02_functions/functions4.rs | 5 -- exercises/02_functions/functions5.rs | 5 -- exercises/03_if/if1.rs | 4 - exercises/03_if/if2.rs | 4 - exercises/03_if/if3.rs | 4 - exercises/04_primitive_types/primitive_types1.rs | 5 +- exercises/04_primitive_types/primitive_types2.rs | 5 -- exercises/04_primitive_types/primitive_types3.rs | 5 -- exercises/04_primitive_types/primitive_types4.rs | 20 ++--- exercises/04_primitive_types/primitive_types5.rs | 5 -- exercises/04_primitive_types/primitive_types6.rs | 24 ++--- exercises/05_vecs/vecs1.rs | 4 - exercises/05_vecs/vecs2.rs | 4 - exercises/06_move_semantics/move_semantics1.rs | 30 ++++--- exercises/06_move_semantics/move_semantics2.rs | 34 +++---- exercises/06_move_semantics/move_semantics3.rs | 30 ++++--- exercises/06_move_semantics/move_semantics4.rs | 34 +++---- exercises/06_move_semantics/move_semantics5.rs | 30 ++++--- exercises/06_move_semantics/move_semantics6.rs | 5 -- exercises/07_structs/structs1.rs | 5 -- exercises/07_structs/structs2.rs | 5 -- exercises/07_structs/structs3.rs | 5 -- exercises/08_enums/enums1.rs | 4 - exercises/08_enums/enums2.rs | 5 -- exercises/08_enums/enums3.rs | 5 -- exercises/09_strings/strings1.rs | 5 -- exercises/09_strings/strings2.rs | 5 -- exercises/09_strings/strings3.rs | 5 -- exercises/09_strings/strings4.rs | 6 +- exercises/10_modules/modules1.rs | 5 -- exercises/10_modules/modules2.rs | 5 -- exercises/10_modules/modules3.rs | 5 -- exercises/11_hashmaps/hashmaps1.rs | 5 -- exercises/11_hashmaps/hashmaps2.rs | 5 -- exercises/11_hashmaps/hashmaps3.rs | 5 -- exercises/12_options/options1.rs | 5 -- exercises/12_options/options2.rs | 5 -- exercises/12_options/options3.rs | 5 -- exercises/13_error_handling/errors1.rs | 5 -- exercises/13_error_handling/errors2.rs | 5 -- exercises/13_error_handling/errors3.rs | 5 -- exercises/13_error_handling/errors4.rs | 26 +++--- exercises/13_error_handling/errors5.rs | 5 -- exercises/13_error_handling/errors6.rs | 5 -- exercises/14_generics/generics1.rs | 5 -- exercises/14_generics/generics2.rs | 5 -- exercises/15_traits/traits1.rs | 5 -- exercises/15_traits/traits2.rs | 4 - exercises/15_traits/traits3.rs | 5 -- exercises/15_traits/traits4.rs | 5 -- exercises/15_traits/traits5.rs | 5 -- exercises/16_lifetimes/lifetimes1.rs | 5 -- exercises/16_lifetimes/lifetimes2.rs | 5 -- exercises/16_lifetimes/lifetimes3.rs | 10 +-- exercises/17_tests/tests1.rs | 5 -- exercises/17_tests/tests2.rs | 5 -- exercises/17_tests/tests3.rs | 5 -- exercises/17_tests/tests4.rs | 5 -- exercises/18_iterators/iterators1.rs | 32 ++++--- exercises/18_iterators/iterators2.rs | 5 -- exercises/18_iterators/iterators3.rs | 5 -- exercises/18_iterators/iterators4.rs | 6 -- exercises/18_iterators/iterators5.rs | 5 -- exercises/19_smart_pointers/arc1.rs | 4 - exercises/19_smart_pointers/box1.rs | 4 - exercises/19_smart_pointers/cow1.rs | 4 - exercises/19_smart_pointers/rc1.rs | 109 ++++++++++++----------- exercises/20_threads/threads1.rs | 5 -- exercises/20_threads/threads2.rs | 5 -- exercises/20_threads/threads3.rs | 38 ++++---- exercises/21_macros/macros1.rs | 5 -- exercises/21_macros/macros2.rs | 5 -- exercises/21_macros/macros3.rs | 5 -- exercises/21_macros/macros4.rs | 5 -- exercises/22_clippy/clippy1.rs | 5 -- exercises/22_clippy/clippy2.rs | 5 -- exercises/22_clippy/clippy3.rs | 3 - exercises/23_conversions/as_ref_mut.rs | 5 -- exercises/23_conversions/from_into.rs | 6 -- exercises/23_conversions/from_str.rs | 5 -- exercises/23_conversions/try_from_into.rs | 5 -- exercises/23_conversions/using_as.rs | 5 -- exercises/quiz1.rs | 31 +++---- exercises/quiz2.rs | 4 - exercises/quiz3.rs | 4 - 96 files changed, 242 insertions(+), 610 deletions(-) (limited to 'exercises/00_intro/intro1.rs') diff --git a/exercises/00_intro/intro1.rs b/exercises/00_intro/intro1.rs index 170d195..7000039 100644 --- a/exercises/00_intro/intro1.rs +++ b/exercises/00_intro/intro1.rs @@ -1,5 +1,3 @@ -// intro1.rs -// // 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, remove the `I AM NOT DONE` comment below. @@ -8,9 +6,6 @@ // reloaded when you change one of the lines below! Try adding a `println!` // line, or try changing what it outputs in your terminal. Try removing a // semicolon and see what happens! -// -// Execute `rustlings hint intro1` or use the `hint` watch subcommand for a -// hint. fn main() { println!("Hello and"); diff --git a/exercises/00_intro/intro2.rs b/exercises/00_intro/intro2.rs index 84e0d75..c7a3ab2 100644 --- a/exercises/00_intro/intro2.rs +++ b/exercises/00_intro/intro2.rs @@ -1,9 +1,4 @@ -// intro2.rs -// // Make the code print a greeting to the world. -// -// Execute `rustlings hint intro2` or use the `hint` watch subcommand for a -// hint. fn main() { printline!("Hello there!") diff --git a/exercises/01_variables/variables1.rs b/exercises/01_variables/variables1.rs index 56408f3..3035bfa 100644 --- a/exercises/01_variables/variables1.rs +++ b/exercises/01_variables/variables1.rs @@ -1,9 +1,4 @@ -// variables1.rs -// // Make me compile! -// -// Execute `rustlings hint variables1` or use the `hint` watch subcommand for a -// hint. fn main() { x = 5; diff --git a/exercises/01_variables/variables2.rs b/exercises/01_variables/variables2.rs index 0f417e0..ce2dd85 100644 --- a/exercises/01_variables/variables2.rs +++ b/exercises/01_variables/variables2.rs @@ -1,8 +1,3 @@ -// variables2.rs -// -// Execute `rustlings hint variables2` or use the `hint` watch subcommand for a -// hint. - fn main() { let x; if x == 10 { diff --git a/exercises/01_variables/variables3.rs b/exercises/01_variables/variables3.rs index 421c6b1..488385b 100644 --- a/exercises/01_variables/variables3.rs +++ b/exercises/01_variables/variables3.rs @@ -1,8 +1,3 @@ -// variables3.rs -// -// Execute `rustlings hint variables3` or use the `hint` watch subcommand for a -// hint. - fn main() { let x: i32; println!("Number {}", x); diff --git a/exercises/01_variables/variables4.rs b/exercises/01_variables/variables4.rs index 68f8f50..67be127 100644 --- a/exercises/01_variables/variables4.rs +++ b/exercises/01_variables/variables4.rs @@ -1,8 +1,3 @@ -// variables4.rs -// -// Execute `rustlings hint variables4` or use the `hint` watch subcommand for a -// hint. - fn main() { let x = 3; println!("Number {}", x); diff --git a/exercises/01_variables/variables5.rs b/exercises/01_variables/variables5.rs index 7014c56..3a74541 100644 --- a/exercises/01_variables/variables5.rs +++ b/exercises/01_variables/variables5.rs @@ -1,8 +1,3 @@ -// variables5.rs -// -// Execute `rustlings hint variables5` or use the `hint` watch subcommand for a -// hint. - fn main() { let number = "T-H-R-E-E"; // don't change this line println!("Spell a Number : {}", number); diff --git a/exercises/01_variables/variables6.rs b/exercises/01_variables/variables6.rs index 9f47682..4746331 100644 --- a/exercises/01_variables/variables6.rs +++ b/exercises/01_variables/variables6.rs @@ -1,8 +1,3 @@ -// variables6.rs -// -// Execute `rustlings hint variables6` or use the `hint` watch subcommand for a -// hint. - const NUMBER = 3; fn main() { println!("Number {}", NUMBER); diff --git a/exercises/02_functions/functions1.rs b/exercises/02_functions/functions1.rs index 2365f91..4e3b103 100644 --- a/exercises/02_functions/functions1.rs +++ b/exercises/02_functions/functions1.rs @@ -1,8 +1,3 @@ -// functions1.rs -// -// Execute `rustlings hint functions1` or use the `hint` watch subcommand for a -// hint. - fn main() { call_me(); } diff --git a/exercises/02_functions/functions2.rs b/exercises/02_functions/functions2.rs index 64dbd66..84e09cd 100644 --- a/exercises/02_functions/functions2.rs +++ b/exercises/02_functions/functions2.rs @@ -1,8 +1,3 @@ -// functions2.rs -// -// Execute `rustlings hint functions2` or use the `hint` watch subcommand for a -// hint. - fn main() { call_me(3); } diff --git a/exercises/02_functions/functions3.rs b/exercises/02_functions/functions3.rs index 5037121..66fb6d3 100644 --- a/exercises/02_functions/functions3.rs +++ b/exercises/02_functions/functions3.rs @@ -1,8 +1,3 @@ -// functions3.rs -// -// Execute `rustlings hint functions3` or use the `hint` watch subcommand for a -// hint. - fn main() { call_me(); } diff --git a/exercises/02_functions/functions4.rs b/exercises/02_functions/functions4.rs index 6b449ed..06d3b1b 100644 --- a/exercises/02_functions/functions4.rs +++ b/exercises/02_functions/functions4.rs @@ -1,12 +1,7 @@ -// functions4.rs -// // This store is having a sale where if the price is an even number, you get 10 // Rustbucks off, but if it's an odd number, it's 3 Rustbucks off. (Don't worry // about the function bodies themselves, we're only interested in the signatures // for now. If anything, this is a good way to peek ahead to future exercises!) -// -// Execute `rustlings hint functions4` or use the `hint` watch subcommand for a -// hint. fn main() { let original_price = 51; diff --git a/exercises/02_functions/functions5.rs b/exercises/02_functions/functions5.rs index 0c96322..3bb5e52 100644 --- a/exercises/02_functions/functions5.rs +++ b/exercises/02_functions/functions5.rs @@ -1,8 +1,3 @@ -// functions5.rs -// -// Execute `rustlings hint functions5` or use the `hint` watch subcommand for a -// hint. - fn main() { let answer = square(3); println!("The square of 3 is {}", answer); diff --git a/exercises/03_if/if1.rs b/exercises/03_if/if1.rs index dbd0d28..52dee0b 100644 --- a/exercises/03_if/if1.rs +++ b/exercises/03_if/if1.rs @@ -1,7 +1,3 @@ -// if1.rs -// -// Execute `rustlings hint if1` or use the `hint` watch subcommand for a hint. - pub fn bigger(a: i32, b: i32) -> i32 { // Complete this function to return the bigger number! // If both numbers are equal, any of them can be returned. diff --git a/exercises/03_if/if2.rs b/exercises/03_if/if2.rs index a1ed5c8..a06bba5 100644 --- a/exercises/03_if/if2.rs +++ b/exercises/03_if/if2.rs @@ -1,9 +1,5 @@ -// if2.rs -// // Step 1: Make me compile! // Step 2: Get the bar_for_fuzz and default_to_baz tests passing! -// -// Execute `rustlings hint if2` or use the `hint` watch subcommand for a hint. pub fn foo_if_fizz(fizzish: &str) -> &str { if fizzish == "fizz" { diff --git a/exercises/03_if/if3.rs b/exercises/03_if/if3.rs index 0b44c5a..1d9b7c2 100644 --- a/exercises/03_if/if3.rs +++ b/exercises/03_if/if3.rs @@ -1,7 +1,3 @@ -// if3.rs -// -// Execute `rustlings hint if3` or use the `hint` watch subcommand for a hint. - pub fn animal_habitat(animal: &str) -> &'static str { let identifier = if animal == "crab" { 1 diff --git a/exercises/04_primitive_types/primitive_types1.rs b/exercises/04_primitive_types/primitive_types1.rs index f9169c8..0002651 100644 --- a/exercises/04_primitive_types/primitive_types1.rs +++ b/exercises/04_primitive_types/primitive_types1.rs @@ -1,7 +1,4 @@ -// primitive_types1.rs -// -// Fill in the rest of the line that has code missing! No hints, there's no -// tricks, just get used to typing these :) +// Fill in the rest of the line that has code missing! fn main() { // Booleans (`bool`) diff --git a/exercises/04_primitive_types/primitive_types2.rs b/exercises/04_primitive_types/primitive_types2.rs index 1911b12..29c7471 100644 --- a/exercises/04_primitive_types/primitive_types2.rs +++ b/exercises/04_primitive_types/primitive_types2.rs @@ -1,8 +1,3 @@ -// primitive_types2.rs -// -// Fill in the rest of the line that has code missing! No hints, there's no -// tricks, just get used to typing these :) - fn main() { // Characters (`char`) diff --git a/exercises/04_primitive_types/primitive_types3.rs b/exercises/04_primitive_types/primitive_types3.rs index 70a8cc2..5095fc4 100644 --- a/exercises/04_primitive_types/primitive_types3.rs +++ b/exercises/04_primitive_types/primitive_types3.rs @@ -1,9 +1,4 @@ -// primitive_types3.rs -// // Create an array with at least 100 elements in it where the ??? is. -// -// Execute `rustlings hint primitive_types3` or use the `hint` watch subcommand -// for a hint. fn main() { let a = ??? diff --git a/exercises/04_primitive_types/primitive_types4.rs b/exercises/04_primitive_types/primitive_types4.rs index f99d889..c583ae1 100644 --- a/exercises/04_primitive_types/primitive_types4.rs +++ b/exercises/04_primitive_types/primitive_types4.rs @@ -1,19 +1,19 @@ -// primitive_types4.rs -// // Get a slice out of Array a where the ??? is so that the test passes. -// -// Execute `rustlings hint primitive_types4` or use the `hint` watch subcommand -// for a hint. fn main() { // You can optionally experiment here. } -#[test] -fn slice_out_of_array() { - let a = [1, 2, 3, 4, 5]; +#[cfg(test)] +mod tests { + use super::*; - let nice_slice = ??? + #[test] + fn slice_out_of_array() { + let a = [1, 2, 3, 4, 5]; - assert_eq!([2, 3, 4], nice_slice) + let nice_slice = ??? + + assert_eq!([2, 3, 4], nice_slice) + } } diff --git a/exercises/04_primitive_types/primitive_types5.rs b/exercises/04_primitive_types/primitive_types5.rs index 5754a3d..f2216a5 100644 --- a/exercises/04_primitive_types/primitive_types5.rs +++ b/exercises/04_primitive_types/primitive_types5.rs @@ -1,9 +1,4 @@ -// primitive_types5.rs -// // Destructure the `cat` tuple so that the println will work. -// -// Execute `rustlings hint primitive_types5` or use the `hint` watch subcommand -// for a hint. fn main() { let cat = ("Furry McFurson", 3.5); diff --git a/exercises/04_primitive_types/primitive_types6.rs b/exercises/04_primitive_types/primitive_types6.rs index 48e84d3..83cec24 100644 --- a/exercises/04_primitive_types/primitive_types6.rs +++ b/exercises/04_primitive_types/primitive_types6.rs @@ -1,21 +1,21 @@ -// primitive_types6.rs -// // Use a tuple index to access the second element of `numbers`. You can put the // expression for the second element where ??? is so that the test passes. -// -// Execute `rustlings hint primitive_types6` or use the `hint` watch subcommand -// for a hint. fn main() { // You can optionally experiment here. } -#[test] -fn indexing_tuple() { - let numbers = (1, 2, 3); - // Replace below ??? with the tuple indexing syntax. - let second = ???; +#[cfg(test)] +mod tests { + use super::*; - assert_eq!(2, second, - "This is not the 2nd number in the tuple!") + #[test] + fn indexing_tuple() { + let numbers = (1, 2, 3); + // Replace below ??? with the tuple indexing syntax. + let second = ???; + + assert_eq!(2, second, + "This is not the 2nd number in the tuple!") + } } diff --git a/exercises/05_vecs/vecs1.rs b/exercises/05_vecs/vecs1.rs index 5f44cb2..ddcad84 100644 --- a/exercises/05_vecs/vecs1.rs +++ b/exercises/05_vecs/vecs1.rs @@ -1,11 +1,7 @@ -// vecs1.rs -// // Your task is to create a `Vec` which holds the exact same elements as in the // array `a`. // // Make me compile and pass the test! -// -// Execute `rustlings hint vecs1` or use the `hint` watch subcommand for a hint. fn array_and_vec() -> ([i32; 4], Vec) { let a = [10, 20, 30, 40]; // a plain array diff --git a/exercises/05_vecs/vecs2.rs b/exercises/05_vecs/vecs2.rs index 1b16f0b..e72209c 100644 --- a/exercises/05_vecs/vecs2.rs +++ b/exercises/05_vecs/vecs2.rs @@ -1,11 +1,7 @@ -// vecs2.rs -// // A Vec of even numbers is given. Your task is to complete the loop so that // each number in the Vec is multiplied by 2. // // Make me pass the test! -// -// Execute `rustlings hint vecs2` or use the `hint` watch subcommand for a hint. fn vec_loop(mut v: Vec) -> Vec { for element in v.iter_mut() { diff --git a/exercises/06_move_semantics/move_semantics1.rs b/exercises/06_move_semantics/move_semantics1.rs index c612ba9..8c3fe3a 100644 --- a/exercises/06_move_semantics/move_semantics1.rs +++ b/exercises/06_move_semantics/move_semantics1.rs @@ -1,21 +1,25 @@ -// move_semantics1.rs -// -// Execute `rustlings hint move_semantics1` or use the `hint` watch subcommand -// for a hint. +fn fill_vec(vec: Vec) -> Vec { + let vec = vec; -#[test] -fn main() { - let vec0 = vec![22, 44, 66]; + vec.push(88); - let vec1 = fill_vec(vec0); + vec +} - assert_eq!(vec1, vec![22, 44, 66, 88]); +fn main() { + // You can optionally experiment here. } -fn fill_vec(vec: Vec) -> Vec { - let vec = vec; +#[cfg(test)] +mod tests { + use super::*; - vec.push(88); + #[test] + fn move_semantics1() { + let vec0 = vec![22, 44, 66]; - vec + let vec1 = fill_vec(vec0); + + assert_eq!(vec1, vec![22, 44, 66, 88]); + } } diff --git a/exercises/06_move_semantics/move_semantics2.rs b/exercises/06_move_semantics/move_semantics2.rs index 3457d11..d087911 100644 --- a/exercises/06_move_semantics/move_semantics2.rs +++ b/exercises/06_move_semantics/move_semantics2.rs @@ -1,19 +1,4 @@ -// move_semantics2.rs -// // Make the test pass by finding a way to keep both Vecs separate! -// -// Execute `rustlings hint move_semantics2` or use the `hint` watch subcommand -// for a hint. - -#[test] -fn main() { - let vec0 = vec![22, 44, 66]; - - let vec1 = fill_vec(vec0); - - assert_eq!(vec0, vec![22, 44, 66]); - assert_eq!(vec1, vec![22, 44, 66, 88]); -} fn fill_vec(vec: Vec) -> Vec { let mut vec = vec; @@ -22,3 +7,22 @@ fn fill_vec(vec: Vec) -> Vec { vec } + +fn main() { + // You can optionally experiment here. +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn move_semantics2() { + let vec0 = vec![22, 44, 66]; + + let vec1 = fill_vec(vec0); + + assert_eq!(vec0, vec![22, 44, 66]); + assert_eq!(vec1, vec![22, 44, 66, 88]); + } +} diff --git a/exercises/06_move_semantics/move_semantics3.rs b/exercises/06_move_semantics/move_semantics3.rs index 9415eb1..24e3597 100644 --- a/exercises/06_move_semantics/move_semantics3.rs +++ b/exercises/06_move_semantics/move_semantics3.rs @@ -1,22 +1,26 @@ -// move_semantics3.rs -// // Make me compile without adding new lines -- just changing existing lines! (no // lines with multiple semicolons necessary!) -// -// Execute `rustlings hint move_semantics3` or use the `hint` watch subcommand -// for a hint. -#[test] -fn main() { - let vec0 = vec![22, 44, 66]; +fn fill_vec(vec: Vec) -> Vec { + vec.push(88); - let vec1 = fill_vec(vec0); + vec +} - assert_eq!(vec1, vec![22, 44, 66, 88]); +fn main() { + // You can optionally experiment here. } -fn fill_vec(vec: Vec) -> Vec { - vec.push(88); +#[cfg(test)] +mod tests { + use super::*; - vec + #[test] + fn move_semantics3() { + let vec0 = vec![22, 44, 66]; + + let vec1 = fill_vec(vec0); + + assert_eq!(vec1, vec![22, 44, 66, 88]); + } } diff --git a/exercises/06_move_semantics/move_semantics4.rs b/exercises/06_move_semantics/move_semantics4.rs index 1509f5d..b662224 100644 --- a/exercises/06_move_semantics/move_semantics4.rs +++ b/exercises/06_move_semantics/move_semantics4.rs @@ -1,20 +1,6 @@ -// move_semantics4.rs -// // Refactor this code so that instead of passing `vec0` into the `fill_vec` // function, the Vector gets created in the function itself and passed back to -// the main function. -// -// Execute `rustlings hint move_semantics4` or use the `hint` watch subcommand -// for a hint. - -#[test] -fn main() { - let vec0 = vec![22, 44, 66]; - - let vec1 = fill_vec(vec0); - - assert_eq!(vec1, vec![22, 44, 66, 88]); -} +// the test function. // `fill_vec()` no longer takes `vec: Vec` as argument - don't change this! fn fill_vec() -> Vec { @@ -25,3 +11,21 @@ fn fill_vec() -> Vec { vec } + +fn main() { + // You can optionally experiment here. +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn move_semantics4() { + let vec0 = vec![22, 44, 66]; + + let vec1 = fill_vec(vec0); + + assert_eq!(vec1, vec![22, 44, 66, 88]); + } +} diff --git a/exercises/06_move_semantics/move_semantics5.rs b/exercises/06_move_semantics/move_semantics5.rs index c84d2fe..b34560a 100644 --- a/exercises/06_move_semantics/move_semantics5.rs +++ b/exercises/06_move_semantics/move_semantics5.rs @@ -1,17 +1,21 @@ -// move_semantics5.rs -// -// Make me compile only by reordering the lines in `main()`, but without adding, +// Make me compile only by reordering the lines in the test, but without adding, // changing or removing any of them. -// -// Execute `rustlings hint move_semantics5` or use the `hint` watch subcommand -// for a hint. -#[test] fn main() { - let mut x = 100; - let y = &mut x; - let z = &mut x; - *y += 100; - *z += 1000; - assert_eq!(x, 1200); + // You can optionally experiment here. +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn move_semantics5() { + let mut x = 100; + let y = &mut x; + let z = &mut x; + *y += 100; + *z += 1000; + assert_eq!(x, 1200); + } } diff --git a/exercises/06_move_semantics/move_semantics6.rs b/exercises/06_move_semantics/move_semantics6.rs index 6059e61..2ad71db 100644 --- a/exercises/06_move_semantics/move_semantics6.rs +++ b/exercises/06_move_semantics/move_semantics6.rs @@ -1,9 +1,4 @@ -// move_semantics6.rs -// // You can't change anything except adding or removing references. -// -// Execute `rustlings hint move_semantics6` or use the `hint` watch subcommand -// for a hint. fn main() { let data = "Rust is great!".to_string(); diff --git a/exercises/07_structs/structs1.rs b/exercises/07_structs/structs1.rs index cd8b81c..62f1421 100644 --- a/exercises/07_structs/structs1.rs +++ b/exercises/07_structs/structs1.rs @@ -1,9 +1,4 @@ -// structs1.rs -// // Address all the TODOs to make the tests pass! -// -// Execute `rustlings hint structs1` or use the `hint` watch subcommand for a -// hint. struct ColorClassicStruct { // TODO: Something goes here diff --git a/exercises/07_structs/structs2.rs b/exercises/07_structs/structs2.rs index 7e61e75..451dbe7 100644 --- a/exercises/07_structs/structs2.rs +++ b/exercises/07_structs/structs2.rs @@ -1,9 +1,4 @@ -// structs2.rs -// // Address all the TODOs to make the tests pass! -// -// Execute `rustlings hint structs2` or use the `hint` watch subcommand for a -// hint. #[derive(Debug)] struct Order { diff --git a/exercises/07_structs/structs3.rs b/exercises/07_structs/structs3.rs index bd562a1..10adb48 100644 --- a/exercises/07_structs/structs3.rs +++ b/exercises/07_structs/structs3.rs @@ -1,11 +1,6 @@ -// structs3.rs -// // Structs contain data, but can also have logic. In this exercise we have // defined the Package struct and we want to test some logic attached to it. // Make the code compile and the tests pass! -// -// Execute `rustlings hint structs3` or use the `hint` watch subcommand for a -// hint. #[derive(Debug)] struct Package { diff --git a/exercises/08_enums/enums1.rs b/exercises/08_enums/enums1.rs index 330269c..d63de83 100644 --- a/exercises/08_enums/enums1.rs +++ b/exercises/08_enums/enums1.rs @@ -1,7 +1,3 @@ -// enums1.rs -// -// No hints this time! ;) - #[derive(Debug)] enum Message { // TODO: define a few types of messages as used below diff --git a/exercises/08_enums/enums2.rs b/exercises/08_enums/enums2.rs index f0e4e6d..f3b803f 100644 --- a/exercises/08_enums/enums2.rs +++ b/exercises/08_enums/enums2.rs @@ -1,8 +1,3 @@ -// enums2.rs -// -// Execute `rustlings hint enums2` or use the `hint` watch subcommand for a -// hint. - #[derive(Debug)] enum Message { // TODO: define the different variants used below diff --git a/exercises/08_enums/enums3.rs b/exercises/08_enums/enums3.rs index 56c04fe..edac3df 100644 --- a/exercises/08_enums/enums3.rs +++ b/exercises/08_enums/enums3.rs @@ -1,9 +1,4 @@ -// enums3.rs -// // Address all the TODOs to make the tests pass! -// -// Execute `rustlings hint enums3` or use the `hint` watch subcommand for a -// hint. enum Message { // TODO: implement the message variant types based on their usage below diff --git a/exercises/09_strings/strings1.rs b/exercises/09_strings/strings1.rs index a1255a3..de762eb 100644 --- a/exercises/09_strings/strings1.rs +++ b/exercises/09_strings/strings1.rs @@ -1,9 +1,4 @@ -// strings1.rs -// // Make me compile without changing the function signature! -// -// Execute `rustlings hint strings1` or use the `hint` watch subcommand for a -// hint. fn main() { let answer = current_favorite_color(); diff --git a/exercises/09_strings/strings2.rs b/exercises/09_strings/strings2.rs index ba76fe6..4768278 100644 --- a/exercises/09_strings/strings2.rs +++ b/exercises/09_strings/strings2.rs @@ -1,9 +1,4 @@ -// strings2.rs -// // Make me compile without changing the function signature! -// -// Execute `rustlings hint strings2` or use the `hint` watch subcommand for a -// hint. fn main() { let word = String::from("green"); // Try not changing this line :) diff --git a/exercises/09_strings/strings3.rs b/exercises/09_strings/strings3.rs index d53f654..f83a531 100644 --- a/exercises/09_strings/strings3.rs +++ b/exercises/09_strings/strings3.rs @@ -1,8 +1,3 @@ -// strings3.rs -// -// Execute `rustlings hint strings3` or use the `hint` watch subcommand for a -// hint. - fn trim_me(input: &str) -> String { // TODO: Remove whitespace from both ends of a string! ??? diff --git a/exercises/09_strings/strings4.rs b/exercises/09_strings/strings4.rs index a034aa4..1f3d88b 100644 --- a/exercises/09_strings/strings4.rs +++ b/exercises/09_strings/strings4.rs @@ -1,11 +1,7 @@ -// strings4.rs -// -// Ok, here are a bunch of values-- some are `String`s, some are `&str`s. Your +// Ok, here are a bunch of values - some are `String`s, some are `&str`s. Your // task is to call one of these two functions on each value depending on what // 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! -// -// No hints this time! fn string_slice(arg: &str) { println!("{}", arg); diff --git a/exercises/10_modules/modules1.rs b/exercises/10_modules/modules1.rs index c750946..931a3e2 100644 --- a/exercises/10_modules/modules1.rs +++ b/exercises/10_modules/modules1.rs @@ -1,8 +1,3 @@ -// modules1.rs -// -// Execute `rustlings hint modules1` or use the `hint` watch subcommand for a -// hint. - mod sausage_factory { // Don't let anybody outside of this module see this! fn get_secret_recipe() -> String { diff --git a/exercises/10_modules/modules2.rs b/exercises/10_modules/modules2.rs index 4d3106c..5f8b0d5 100644 --- a/exercises/10_modules/modules2.rs +++ b/exercises/10_modules/modules2.rs @@ -1,11 +1,6 @@ -// modules2.rs -// // You can bring module paths into scopes and provide new names for them with // the 'use' and 'as' keywords. Fix these 'use' statements to make the code // compile. -// -// Execute `rustlings hint modules2` or use the `hint` watch subcommand for a -// hint. mod delicious_snacks { // TODO: Fix these use statements diff --git a/exercises/10_modules/modules3.rs b/exercises/10_modules/modules3.rs index c211a76..eff24a9 100644 --- a/exercises/10_modules/modules3.rs +++ b/exercises/10_modules/modules3.rs @@ -1,12 +1,7 @@ -// modules3.rs -// // You can use the 'use' keyword to bring module paths from modules from // anywhere and especially from the Rust standard library into your scope. Bring // SystemTime and UNIX_EPOCH from the std::time module. Bonus style points if // you can do it with one line! -// -// Execute `rustlings hint modules3` or use the `hint` watch subcommand for a -// hint. // TODO: Complete this use statement use ??? diff --git a/exercises/11_hashmaps/hashmaps1.rs b/exercises/11_hashmaps/hashmaps1.rs index 51146df..e646ed7 100644 --- a/exercises/11_hashmaps/hashmaps1.rs +++ b/exercises/11_hashmaps/hashmaps1.rs @@ -1,5 +1,3 @@ -// hashmaps1.rs -// // A basket of fruits in the form of a hash map needs to be defined. The key // represents the name of the fruit and the value represents how many of that // particular fruit is in the basket. You have to put at least three different @@ -7,9 +5,6 @@ // of all the fruits should be at least five. // // Make me compile and pass the tests! -// -// Execute `rustlings hint hashmaps1` or use the `hint` watch subcommand for a -// hint. use std::collections::HashMap; diff --git a/exercises/11_hashmaps/hashmaps2.rs b/exercises/11_hashmaps/hashmaps2.rs index 47983f6..e6380d9 100644 --- a/exercises/11_hashmaps/hashmaps2.rs +++ b/exercises/11_hashmaps/hashmaps2.rs @@ -1,5 +1,3 @@ -// hashmaps2.rs -// // We're collecting different fruits to bake a delicious fruit cake. For this, // we have a basket, which we'll represent in the form of a hash map. The key // represents the name of each fruit we collect and the value represents how @@ -10,9 +8,6 @@ // to insert any more of these fruits! // // Make me pass the tests! -// -// Execute `rustlings hint hashmaps2` or use the `hint` watch subcommand for a -// hint. use std::collections::HashMap; diff --git a/exercises/11_hashmaps/hashmaps3.rs b/exercises/11_hashmaps/hashmaps3.rs index 3322909..070c370 100644 --- a/exercises/11_hashmaps/hashmaps3.rs +++ b/exercises/11_hashmaps/hashmaps3.rs @@ -1,5 +1,3 @@ -// hashmaps3.rs -// // A list of scores (one per line) of a soccer match is given. Each line is of // the form : ",,," // Example: England,France,4,2 (England scored 4 goals, France 2). @@ -11,9 +9,6 @@ // complete it to pass the test. // // Make me pass the tests! -// -// Execute `rustlings hint hashmaps3` or use the `hint` watch subcommand for a -// hint. use std::collections::HashMap; diff --git a/exercises/12_options/options1.rs b/exercises/12_options/options1.rs index aecb123..b7cf7b0 100644 --- a/exercises/12_options/options1.rs +++ b/exercises/12_options/options1.rs @@ -1,8 +1,3 @@ -// options1.rs -// -// Execute `rustlings hint options1` or use the `hint` watch subcommand for a -// hint. - // This function returns how much icecream there is left in the fridge. // If it's before 10PM, there's 5 scoops left. At 10PM, someone eats it // all, so there'll be no more left :( diff --git a/exercises/12_options/options2.rs b/exercises/12_options/options2.rs index d183d1d..01f84c5 100644 --- a/exercises/12_options/options2.rs +++ b/exercises/12_options/options2.rs @@ -1,8 +1,3 @@ -// options2.rs -// -// Execute `rustlings hint options2` or use the `hint` watch subcommand for a -// hint. - fn main() { // You can optionally experiment here. } diff --git a/exercises/12_options/options3.rs b/exercises/12_options/options3.rs index 7922ef9..5b70a79 100644 --- a/exercises/12_options/options3.rs +++ b/exercises/12_options/options3.rs @@ -1,8 +1,3 @@ -// options3.rs -// -// Execute `rustlings hint options3` or use the `hint` watch subcommand for a -// hint. - struct Point { x: i32, y: i32, diff --git a/exercises/13_error_handling/errors1.rs b/exercises/13_error_handling/errors1.rs index 7991c42..15a3716 100644 --- a/exercises/13_error_handling/errors1.rs +++ b/exercises/13_error_handling/errors1.rs @@ -1,13 +1,8 @@ -// errors1.rs -// // This function refuses to generate text to be printed on a nametag if you pass // it an empty string. It'd be nicer if it explained what the problem was, // instead of just sometimes returning `None`. Thankfully, Rust has a similar // construct to `Option` that can be used to express error conditions. Let's use // it! -// -// Execute `rustlings hint errors1` or use the `hint` watch subcommand for a -// hint. fn main() { // You can optionally experiment here. diff --git a/exercises/13_error_handling/errors2.rs b/exercises/13_error_handling/errors2.rs index 051516b..e39aa95 100644 --- a/exercises/13_error_handling/errors2.rs +++ b/exercises/13_error_handling/errors2.rs @@ -1,5 +1,3 @@ -// errors2.rs -// // Say we're writing a game where you can buy items with tokens. All items cost // 5 tokens, and whenever you purchase items there is a processing fee of 1 // token. A player of the game will type in how many items they want to buy, and @@ -15,9 +13,6 @@ // // There are at least two ways to implement this that are both correct-- but one // is a lot shorter! -// -// Execute `rustlings hint errors2` or use the `hint` watch subcommand for a -// hint. use std::num::ParseIntError; diff --git a/exercises/13_error_handling/errors3.rs b/exercises/13_error_handling/errors3.rs index 56bb31b..5661f17 100644 --- a/exercises/13_error_handling/errors3.rs +++ b/exercises/13_error_handling/errors3.rs @@ -1,11 +1,6 @@ -// errors3.rs -// // This is a program that is trying to use a completed version of the // `total_cost` function from the previous exercise. It's not working though! // Why not? What should we do to fix it? -// -// Execute `rustlings hint errors3` or use the `hint` watch subcommand for a -// hint. use std::num::ParseIntError; diff --git a/exercises/13_error_handling/errors4.rs b/exercises/13_error_handling/errors4.rs index 9449417..993d42a 100644 --- a/exercises/13_error_handling/errors4.rs +++ b/exercises/13_error_handling/errors4.rs @@ -1,8 +1,3 @@ -// errors4.rs -// -// Execute `rustlings hint errors4` or use the `hint` watch subcommand for a -// hint. - #[derive(PartialEq, Debug)] struct PositiveNonzeroInteger(u64); @@ -23,12 +18,17 @@ fn main() { // You can optionally experiment here. } -#[test] -fn test_creation() { - assert!(PositiveNonzeroInteger::new(10).is_ok()); - assert_eq!( - Err(CreationError::Negative), - PositiveNonzeroInteger::new(-10) - ); - assert_eq!(Err(CreationError::Zero), PositiveNonzeroInteger::new(0)); +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_creation() { + assert!(PositiveNonzeroInteger::new(10).is_ok()); + assert_eq!( + Err(CreationError::Negative), + PositiveNonzeroInteger::new(-10) + ); + assert_eq!(Err(CreationError::Zero), PositiveNonzeroInteger::new(0)); + } } diff --git a/exercises/13_error_handling/errors5.rs b/exercises/13_error_handling/errors5.rs index 0bcb4b8..7192562 100644 --- a/exercises/13_error_handling/errors5.rs +++ b/exercises/13_error_handling/errors5.rs @@ -1,5 +1,3 @@ -// errors5.rs -// // This program uses an altered version of the code from errors4. // // This exercise uses some concepts that we won't get to until later in the @@ -18,9 +16,6 @@ // // What can we use to describe both errors? In other words, is there a trait // which both errors implement? -// -// Execute `rustlings hint errors5` or use the `hint` watch subcommand for a -// hint. use std::error; use std::fmt; diff --git a/exercises/13_error_handling/errors6.rs b/exercises/13_error_handling/errors6.rs index 363a3b9..8b08e08 100644 --- a/exercises/13_error_handling/errors6.rs +++ b/exercises/13_error_handling/errors6.rs @@ -1,13 +1,8 @@ -// errors6.rs -// // Using catch-all error types like `Box` isn't recommended // for library code, where callers might want to make decisions based on the // error content, instead of printing it out or propagating it further. Here, we // define a custom error type to make it possible for callers to decide what to // do next when our function returns an error. -// -// Execute `rustlings hint errors6` or use the `hint` watch subcommand for a -// hint. use std::num::ParseIntError; diff --git a/exercises/14_generics/generics1.rs b/exercises/14_generics/generics1.rs index 545fd95..c023e64 100644 --- a/exercises/14_generics/generics1.rs +++ b/exercises/14_generics/generics1.rs @@ -1,10 +1,5 @@ -// generics1.rs -// // This shopping list program isn't compiling! Use your knowledge of generics to // fix it. -// -// Execute `rustlings hint generics1` or use the `hint` watch subcommand for a -// hint. fn main() { let mut shopping_list: Vec = Vec::new(); diff --git a/exercises/14_generics/generics2.rs b/exercises/14_generics/generics2.rs index 068468b..cbb9b5f 100644 --- a/exercises/14_generics/generics2.rs +++ b/exercises/14_generics/generics2.rs @@ -1,10 +1,5 @@ -// generics2.rs -// // This powerful wrapper provides the ability to store a positive integer value. // Rewrite it using generics so that it supports wrapping ANY type. -// -// Execute `rustlings hint generics2` or use the `hint` watch subcommand for a -// hint. struct Wrapper { value: u32, diff --git a/exercises/15_traits/traits1.rs b/exercises/15_traits/traits1.rs index c51d3b8..b17c9c6 100644 --- a/exercises/15_traits/traits1.rs +++ b/exercises/15_traits/traits1.rs @@ -1,11 +1,6 @@ -// traits1.rs -// // Time to implement some traits! Your task is to implement the trait // `AppendBar` for the type `String`. The trait AppendBar has only one function, // which appends "Bar" to any object implementing this trait. -// -// Execute `rustlings hint traits1` or use the `hint` watch subcommand for a -// hint. trait AppendBar { fn append_bar(self) -> Self; diff --git a/exercises/15_traits/traits2.rs b/exercises/15_traits/traits2.rs index 18ebcb0..170779b 100644 --- a/exercises/15_traits/traits2.rs +++ b/exercises/15_traits/traits2.rs @@ -1,12 +1,8 @@ -// traits2.rs -// // Your task is to implement the trait `AppendBar` for a vector of strings. To // implement this trait, consider for a moment what it means to 'append "Bar"' // to a vector of strings. // // No boiler plate code this time, you can do this! -// -// Execute `rustlings hint traits2` or use the `hint` watch subcommand for a hint. trait AppendBar { fn append_bar(self) -> Self; diff --git a/exercises/15_traits/traits3.rs b/exercises/15_traits/traits3.rs index 8412afa..9a2365a 100644 --- a/exercises/15_traits/traits3.rs +++ b/exercises/15_traits/traits3.rs @@ -1,12 +1,7 @@ -// traits3.rs -// // Your task is to implement the Licensed trait for both structures and have // them return the same information without writing the same function twice. // // Consider what you can add to the Licensed trait. -// -// Execute `rustlings hint traits3` or use the `hint` watch subcommand for a -// hint. pub trait Licensed { fn licensing_info(&self) -> String; diff --git a/exercises/15_traits/traits4.rs b/exercises/15_traits/traits4.rs index 18db0d6..7af30b5 100644 --- a/exercises/15_traits/traits4.rs +++ b/exercises/15_traits/traits4.rs @@ -1,11 +1,6 @@ -// traits4.rs -// // Your task is to replace the '??' sections so the code compiles. // // Don't change any line other than the marked one. -// -// Execute `rustlings hint traits4` or use the `hint` watch subcommand for a -// hint. pub trait Licensed { fn licensing_info(&self) -> String { diff --git a/exercises/15_traits/traits5.rs b/exercises/15_traits/traits5.rs index f258d32..9a45bb7 100644 --- a/exercises/15_traits/traits5.rs +++ b/exercises/15_traits/traits5.rs @@ -1,11 +1,6 @@ -// traits5.rs -// // Your task is to replace the '??' sections so the code compiles. // // Don't change any line other than the marked one. -// -// Execute `rustlings hint traits5` or use the `hint` watch subcommand for a -// hint. pub trait SomeTrait { fn some_function(&self) -> bool { diff --git a/exercises/16_lifetimes/lifetimes1.rs b/exercises/16_lifetimes/lifetimes1.rs index 4f544b4..d34f3ab 100644 --- a/exercises/16_lifetimes/lifetimes1.rs +++ b/exercises/16_lifetimes/lifetimes1.rs @@ -1,12 +1,7 @@ -// lifetimes1.rs -// // The Rust compiler needs to know how to check whether supplied references are // valid, so that it can let the programmer know if a reference is at risk of // going out of scope before it is used. Remember, references are borrows and do // not own their own data. What if their owner goes out of scope? -// -// Execute `rustlings hint lifetimes1` or use the `hint` watch subcommand for a -// hint. fn longest(x: &str, y: &str) -> &str { if x.len() > y.len() { diff --git a/exercises/16_lifetimes/lifetimes2.rs b/exercises/16_lifetimes/lifetimes2.rs index 33b5565..6e329e6 100644 --- a/exercises/16_lifetimes/lifetimes2.rs +++ b/exercises/16_lifetimes/lifetimes2.rs @@ -1,10 +1,5 @@ -// lifetimes2.rs -// // So if the compiler is just validating the references passed to the annotated // parameters and the return type, what do we need to change? -// -// Execute `rustlings hint lifetimes2` or use the `hint` watch subcommand for a -// hint. fn longest<'a>(x: &'a str, y: &'a str) -> &'a str { if x.len() > y.len() { diff --git a/exercises/16_lifetimes/lifetimes3.rs b/exercises/16_lifetimes/lifetimes3.rs index de6005e..9b631ca 100644 --- a/exercises/16_lifetimes/lifetimes3.rs +++ b/exercises/16_lifetimes/lifetimes3.rs @@ -1,9 +1,4 @@ -// lifetimes3.rs -// // Lifetimes are also needed when structs hold references. -// -// Execute `rustlings hint lifetimes3` or use the `hint` watch subcommand for a -// hint. struct Book { author: &str, @@ -13,7 +8,10 @@ struct Book { fn main() { let name = String::from("Jill Smith"); let title = String::from("Fish Flying"); - let book = Book { author: &name, title: &title }; + let book = Book { + author: &name, + title: &title, + }; println!("{} by {}", book.title, book.author); } diff --git a/exercises/17_tests/tests1.rs b/exercises/17_tests/tests1.rs index d32ace1..854a135 100644 --- a/exercises/17_tests/tests1.rs +++ b/exercises/17_tests/tests1.rs @@ -1,14 +1,9 @@ -// tests1.rs -// // Tests are important to ensure that your code does what you think it should // do. Tests can be run on this file with the following command: rustlings run // tests1 // // This test has a problem with it -- make the test compile! Make the test pass! // Make the test fail! -// -// Execute `rustlings hint tests1` or use the `hint` watch subcommand for a -// hint. fn main() { // You can optionally experiment here. diff --git a/exercises/17_tests/tests2.rs b/exercises/17_tests/tests2.rs index 501c44b..f0899e1 100644 --- a/exercises/17_tests/tests2.rs +++ b/exercises/17_tests/tests2.rs @@ -1,10 +1,5 @@ -// tests2.rs -// // This test has a problem with it -- make the test compile! Make the test pass! // Make the test fail! -// -// Execute `rustlings hint tests2` or use the `hint` watch subcommand for a -// hint. fn main() { // You can optionally experiment here. diff --git a/exercises/17_tests/tests3.rs b/exercises/17_tests/tests3.rs index a2093cf..3b4e199 100644 --- a/exercises/17_tests/tests3.rs +++ b/exercises/17_tests/tests3.rs @@ -1,11 +1,6 @@ -// tests3.rs -// // This test isn't testing our function -- make it do that in such a way that // 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)`. -// -// Execute `rustlings hint tests3` or use the `hint` watch subcommand for a -// hint. pub fn is_even(num: i32) -> bool { num % 2 == 0 diff --git a/exercises/17_tests/tests4.rs b/exercises/17_tests/tests4.rs index a50323c..35a9a3b 100644 --- a/exercises/17_tests/tests4.rs +++ b/exercises/17_tests/tests4.rs @@ -1,9 +1,4 @@ -// tests4.rs -// // Make sure that we're testing for the correct conditions! -// -// Execute `rustlings hint tests4` or use the `hint` watch subcommand for a -// hint. struct Rectangle { width: i32, diff --git a/exercises/18_iterators/iterators1.rs b/exercises/18_iterators/iterators1.rs index 7ec7da2..52b704d 100644 --- a/exercises/18_iterators/iterators1.rs +++ b/exercises/18_iterators/iterators1.rs @@ -1,24 +1,28 @@ -// iterators1.rs -// // When performing operations on elements within a collection, iterators are // essential. This module helps you get familiar with the structure of using an // iterator and how to go through elements within an iterable collection. // // Make me compile by filling in the `???`s -// -// Execute `rustlings hint iterators1` or use the `hint` watch subcommand for a -// hint. -#[test] fn main() { - let my_fav_fruits = vec!["banana", "custard apple", "avocado", "peach", "raspberry"]; + // You can optionally experiment here. +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn iterators() { + let my_fav_fruits = vec!["banana", "custard apple", "avocado", "peach", "raspberry"]; - let mut my_iterable_fav_fruits = ???; // TODO: Step 1 + let mut my_iterable_fav_fruits = ???; // TODO: Step 1 - assert_eq!(my_iterable_fav_fruits.next(), Some(&"banana")); - assert_eq!(my_iterable_fav_fruits.next(), ???); // TODO: Step 2 - assert_eq!(my_iterable_fav_fruits.next(), Some(&"avocado")); - assert_eq!(my_iterable_fav_fruits.next(), ???); // TODO: Step 3 - assert_eq!(my_iterable_fav_fruits.next(), Some(&"raspberry")); - assert_eq!(my_iterable_fav_fruits.next(), ???); // TODO: Step 4 + assert_eq!(my_iterable_fav_fruits.next(), Some(&"banana")); + assert_eq!(my_iterable_fav_fruits.next(), ???); // TODO: Step 2 + assert_eq!(my_iterable_fav_fruits.next(), Some(&"avocado")); + assert_eq!(my_iterable_fav_fruits.next(), ???); // TODO: Step 3 + assert_eq!(my_iterable_fav_fruits.next(), Some(&"raspberry")); + assert_eq!(my_iterable_fav_fruits.next(), ???); // TODO: Step 4 + } } diff --git a/exercises/18_iterators/iterators2.rs b/exercises/18_iterators/iterators2.rs index 0ebd69a..df1fa83 100644 --- a/exercises/18_iterators/iterators2.rs +++ b/exercises/18_iterators/iterators2.rs @@ -1,10 +1,5 @@ -// iterators2.rs -// // In this exercise, you'll learn some of the unique advantages that iterators // can offer. Follow the steps to complete the exercise. -// -// Execute `rustlings hint iterators2` or use the `hint` watch subcommand for a -// hint. // Step 1. // Complete the `capitalize_first` function. diff --git a/exercises/18_iterators/iterators3.rs b/exercises/18_iterators/iterators3.rs index 3f5923c..9f106aa 100644 --- a/exercises/18_iterators/iterators3.rs +++ b/exercises/18_iterators/iterators3.rs @@ -1,13 +1,8 @@ -// iterators3.rs -// // This is a bigger exercise than most of the others! You can do it! Here is // your mission, should you choose to accept it: // 1. Complete the divide function to get the first four tests to pass. // 2. Get the remaining tests to pass by completing the result_with_list and // list_of_results functions. -// -// Execute `rustlings hint iterators3` or use the `hint` watch subcommand for a -// hint. #[derive(Debug, PartialEq, Eq)] pub enum DivisionError { diff --git a/exercises/18_iterators/iterators4.rs b/exercises/18_iterators/iterators4.rs index 8fc8792..60c7b8d 100644 --- a/exercises/18_iterators/iterators4.rs +++ b/exercises/18_iterators/iterators4.rs @@ -1,8 +1,3 @@ -// iterators4.rs -// -// Execute `rustlings hint iterators4` or use the `hint` watch subcommand for a -// hint. - pub fn factorial(num: u64) -> u64 { // Complete this function to return the factorial of num // Do not use: @@ -12,7 +7,6 @@ pub fn factorial(num: u64) -> u64 { // - additional variables // For an extra challenge, don't use: // - recursion - // Execute `rustlings hint iterators4` for hints. } fn main() { diff --git a/exercises/18_iterators/iterators5.rs b/exercises/18_iterators/iterators5.rs index 2604004..4f052d5 100644 --- a/exercises/18_iterators/iterators5.rs +++ b/exercises/18_iterators/iterators5.rs @@ -1,5 +1,3 @@ -// iterators5.rs -// // Let's define a simple model to track Rustlings exercise progress. Progress // will be modelled using a hash map. The name of the exercise is the key and // the progress is the value. Two counting functions were created to count the @@ -7,9 +5,6 @@ // functionality using iterators. Try not to use imperative loops (for, while). // Only the two iterator methods (count_iterator and count_collection_iterator) // need to be modified. -// -// Execute `rustlings hint iterators5` or use the `hint` watch subcommand for a -// hint. use std::collections::HashMap; diff --git a/exercises/19_smart_pointers/arc1.rs b/exercises/19_smart_pointers/arc1.rs index 0647eea..7b31fa8 100644 --- a/exercises/19_smart_pointers/arc1.rs +++ b/exercises/19_smart_pointers/arc1.rs @@ -1,5 +1,3 @@ -// arc1.rs -// // In this exercise, we are given a Vec of u32 called "numbers" with values // ranging from 0 to 99 -- [ 0, 1, 2, ..., 98, 99 ] We would like to use this // set of numbers within 8 different threads simultaneously. Each thread is @@ -18,8 +16,6 @@ // first TODO comment is, and create an initial binding for `child_numbers` // where the second TODO comment is. Try not to create any copies of the // `numbers` Vec! -// -// Execute `rustlings hint arc1` or use the `hint` watch subcommand for a hint. #![forbid(unused_imports)] // Do not change this, (or the next) line. use std::sync::Arc; diff --git a/exercises/19_smart_pointers/box1.rs b/exercises/19_smart_pointers/box1.rs index 2abc024..226a117 100644 --- a/exercises/19_smart_pointers/box1.rs +++ b/exercises/19_smart_pointers/box1.rs @@ -1,5 +1,3 @@ -// box1.rs -// // At compile time, Rust needs to know how much space a type takes up. This // becomes problematic for recursive types, where a value can have as part of // itself another value of the same type. To get around the issue, we can use a @@ -15,8 +13,6 @@ // Step 2: create both empty and non-empty cons lists by replacing `todo!()` // // Note: the tests should not be changed -// -// Execute `rustlings hint box1` or use the `hint` watch subcommand for a hint. #[derive(PartialEq, Debug)] pub enum List { diff --git a/exercises/19_smart_pointers/cow1.rs b/exercises/19_smart_pointers/cow1.rs index 51e5fdb..754c0ba 100644 --- a/exercises/19_smart_pointers/cow1.rs +++ b/exercises/19_smart_pointers/cow1.rs @@ -1,5 +1,3 @@ -// cow1.rs -// // This exercise explores the Cow, or Clone-On-Write type. Cow is a // clone-on-write smart pointer. It can enclose and provide immutable access to // borrowed data, and clone the data lazily when mutation or ownership is @@ -9,8 +7,6 @@ // This exercise is meant to show you what to expect when passing data to Cow. // Fix the unit tests by checking for Cow::Owned(_) and Cow::Borrowed(_) at the // TODO markers. -// -// Execute `rustlings hint cow1` or use the `hint` watch subcommand for a hint. use std::borrow::Cow; diff --git a/exercises/19_smart_pointers/rc1.rs b/exercises/19_smart_pointers/rc1.rs index e96e625..19de3db 100644 --- a/exercises/19_smart_pointers/rc1.rs +++ b/exercises/19_smart_pointers/rc1.rs @@ -1,5 +1,3 @@ -// rc1.rs -// // In this exercise, we want to express the concept of multiple owners via the // Rc type. This is a model of our solar system - there is a Sun type and // multiple Planets. The Planets take ownership of the sun, indicating that they @@ -7,8 +5,6 @@ // // Make this code compile by using the proper Rc primitives to express that the // sun has multiple owners. -// -// Execute `rustlings hint rc1` or use the `hint` watch subcommand for a hint. use std::rc::Rc; @@ -33,71 +29,80 @@ impl Planet { } } -#[test] fn main() { - let sun = Rc::new(Sun {}); - println!("reference count = {}", Rc::strong_count(&sun)); // 1 reference + // You can optionally experiment here. +} - let mercury = Planet::Mercury(Rc::clone(&sun)); - println!("reference count = {}", Rc::strong_count(&sun)); // 2 references - mercury.details(); +#[cfg(test)] +mod tests { + use super::*; - let venus = Planet::Venus(Rc::clone(&sun)); - println!("reference count = {}", Rc::strong_count(&sun)); // 3 references - venus.details(); + #[test] + fn rc1() { + let sun = Rc::new(Sun {}); + println!("reference count = {}", Rc::strong_count(&sun)); // 1 reference - let earth = Planet::Earth(Rc::clone(&sun)); - println!("reference count = {}", Rc::strong_count(&sun)); // 4 references - earth.details(); + let mercury = Planet::Mercury(Rc::clone(&sun)); + println!("reference count = {}", Rc::strong_count(&sun)); // 2 references + mercury.details(); - let mars = Planet::Mars(Rc::clone(&sun)); - println!("reference count = {}", Rc::strong_count(&sun)); // 5 references - mars.details(); + let venus = Planet::Venus(Rc::clone(&sun)); + println!("reference count = {}", Rc::strong_count(&sun)); // 3 references + venus.details(); - let jupiter = Planet::Jupiter(Rc::clone(&sun)); - println!("reference count = {}", Rc::strong_count(&sun)); // 6 references - jupiter.details(); + let earth = Planet::Earth(Rc::clone(&sun)); + println!("reference count = {}", Rc::strong_count(&sun)); // 4 references + earth.details(); - // TODO - let saturn = Planet::Saturn(Rc::new(Sun {})); - println!("reference count = {}", Rc::strong_count(&sun)); // 7 references - saturn.details(); + let mars = Planet::Mars(Rc::clone(&sun)); + println!("reference count = {}", Rc::strong_count(&sun)); // 5 references + mars.details(); - // TODO - let uranus = Planet::Uranus(Rc::new(Sun {})); - println!("reference count = {}", Rc::strong_count(&sun)); // 8 references - uranus.details(); + let jupiter = Planet::Jupiter(Rc::clone(&sun)); + println!("reference count = {}", Rc::strong_count(&sun)); // 6 references + jupiter.details(); - // TODO - let neptune = Planet::Neptune(Rc::new(Sun {})); - println!("reference count = {}", Rc::strong_count(&sun)); // 9 references - neptune.details(); + // TODO + let saturn = Planet::Saturn(Rc::new(Sun {})); + println!("reference count = {}", Rc::strong_count(&sun)); // 7 references + saturn.details(); - assert_eq!(Rc::strong_count(&sun), 9); + // TODO + let uranus = Planet::Uranus(Rc::new(Sun {})); + println!("reference count = {}", Rc::strong_count(&sun)); // 8 references + uranus.details(); - drop(neptune); - println!("reference count = {}", Rc::strong_count(&sun)); // 8 references + // TODO + let neptune = Planet::Neptune(Rc::new(Sun {})); + println!("reference count = {}", Rc::strong_count(&sun)); // 9 references + neptune.details(); - drop(uranus); - println!("reference count = {}", Rc::strong_count(&sun)); // 7 references + assert_eq!(Rc::strong_count(&sun), 9); - drop(saturn); - println!("reference count = {}", Rc::strong_count(&sun)); // 6 references + drop(neptune); + println!("reference count = {}", Rc::strong_count(&sun)); // 8 references - drop(jupiter); - println!("reference count = {}", Rc::strong_count(&sun)); // 5 references + drop(uranus); + println!("reference count = {}", Rc::strong_count(&sun)); // 7 references - drop(mars); - println!("reference count = {}", Rc::strong_count(&sun)); // 4 references + drop(saturn); + println!("reference count = {}", Rc::strong_count(&sun)); // 6 references - // TODO - println!("reference count = {}", Rc::strong_count(&sun)); // 3 references + drop(jupiter); + println!("reference count = {}", Rc::strong_count(&sun)); // 5 references - // TODO - println!("reference count = {}", Rc::strong_count(&sun)); // 2 references + drop(mars); + println!("reference count = {}", Rc::strong_count(&sun)); // 4 references - // TODO - println!("reference count = {}", Rc::strong_count(&sun)); // 1 reference + // TODO + println!("reference count = {}", Rc::strong_count(&sun)); // 3 references - assert_eq!(Rc::strong_count(&sun), 1); + // TODO + println!("reference count = {}", Rc::strong_count(&sun)); // 2 references + + // TODO + println!("reference count = {}", Rc::strong_count(&sun)); // 1 reference + + assert_eq!(Rc::strong_count(&sun), 1); + } } diff --git a/exercises/20_threads/threads1.rs b/exercises/20_threads/threads1.rs index be1301d..bf0b8e0 100644 --- a/exercises/20_threads/threads1.rs +++ b/exercises/20_threads/threads1.rs @@ -1,12 +1,7 @@ -// threads1.rs -// // This program spawns multiple threads that each run for at least 250ms, and // each thread returns how much time they took to complete. The program should // wait until all the spawned threads have finished and should collect their // return values into a vector. -// -// Execute `rustlings hint threads1` or use the `hint` watch subcommand for a -// hint. use std::thread; use std::time::{Duration, Instant}; diff --git a/exercises/20_threads/threads2.rs b/exercises/20_threads/threads2.rs index 13cb840..2bdeba9 100644 --- a/exercises/20_threads/threads2.rs +++ b/exercises/20_threads/threads2.rs @@ -1,11 +1,6 @@ -// threads2.rs -// // Building on the last exercise, we want all of the threads to complete their // work but this time the spawned threads need to be in charge of updating a // shared value: JobStatus.jobs_completed -// -// Execute `rustlings hint threads2` or use the `hint` watch subcommand for a -// hint. use std::sync::Arc; use std::thread; diff --git a/exercises/20_threads/threads3.rs b/exercises/20_threads/threads3.rs index 35b914a..13abc45 100644 --- a/exercises/20_threads/threads3.rs +++ b/exercises/20_threads/threads3.rs @@ -1,8 +1,3 @@ -// threads3.rs -// -// Execute `rustlings hint threads3` or use the `hint` watch subcommand for a -// hint. - use std::sync::mpsc; use std::sync::Arc; use std::thread; @@ -42,20 +37,29 @@ fn send_tx(q: Queue, tx: mpsc::Sender) -> () { }); } -#[test] fn main() { - let (tx, rx) = mpsc::channel(); - let queue = Queue::new(); - let queue_length = queue.length; + // You can optionally experiment here. +} - send_tx(queue, tx); +#[cfg(test)] +mod tests { + use super::*; - let mut total_received: u32 = 0; - for received in rx { - println!("Got: {}", received); - total_received += 1; - } + #[test] + fn threads3() { + let (tx, rx) = mpsc::channel(); + let queue = Queue::new(); + let queue_length = queue.length; + + send_tx(queue, tx); - println!("total numbers received: {}", total_received); - assert_eq!(total_received, queue_length) + let mut total_received: u32 = 0; + for received in rx { + println!("Got: {}", received); + total_received += 1; + } + + println!("total numbers received: {}", total_received); + assert_eq!(total_received, queue_length) + } } diff --git a/exercises/21_macros/macros1.rs b/exercises/21_macros/macros1.rs index 65986db..1d415cb 100644 --- a/exercises/21_macros/macros1.rs +++ b/exercises/21_macros/macros1.rs @@ -1,8 +1,3 @@ -// macros1.rs -// -// Execute `rustlings hint macros1` or use the `hint` watch subcommand for a -// hint. - macro_rules! my_macro { () => { println!("Check out my macro!"); diff --git a/exercises/21_macros/macros2.rs b/exercises/21_macros/macros2.rs index b7c37fd..f16712b 100644 --- a/exercises/21_macros/macros2.rs +++ b/exercises/21_macros/macros2.rs @@ -1,8 +1,3 @@ -// macros2.rs -// -// Execute `rustlings hint macros2` or use the `hint` watch subcommand for a -// hint. - fn main() { my_macro!(); } diff --git a/exercises/21_macros/macros3.rs b/exercises/21_macros/macros3.rs index 92a1922..405c397 100644 --- a/exercises/21_macros/macros3.rs +++ b/exercises/21_macros/macros3.rs @@ -1,9 +1,4 @@ -// macros3.rs -// // Make me compile, without taking the macro out of the module! -// -// Execute `rustlings hint macros3` or use the `hint` watch subcommand for a -// hint. mod macros { macro_rules! my_macro { diff --git a/exercises/21_macros/macros4.rs b/exercises/21_macros/macros4.rs index 83a6e44..03ece08 100644 --- a/exercises/21_macros/macros4.rs +++ b/exercises/21_macros/macros4.rs @@ -1,8 +1,3 @@ -// macros4.rs -// -// Execute `rustlings hint macros4` or use the `hint` watch subcommand for a -// hint. - #[rustfmt::skip] macro_rules! my_macro { () => { diff --git a/exercises/22_clippy/clippy1.rs b/exercises/22_clippy/clippy1.rs index 1e0f42e..f1eaa83 100644 --- a/exercises/22_clippy/clippy1.rs +++ b/exercises/22_clippy/clippy1.rs @@ -1,13 +1,8 @@ -// clippy1.rs -// // The Clippy tool is a collection of lints to analyze your code so you can // catch common mistakes and improve your Rust code. // // For these exercises the code will fail to compile when there are Clippy // warnings. Check Clippy's suggestions from the output to solve the exercise. -// -// Execute `rustlings hint clippy1` or use the `hint` watch subcommand for a -// hint. use std::f32; diff --git a/exercises/22_clippy/clippy2.rs b/exercises/22_clippy/clippy2.rs index 37ac089..c7d400d 100644 --- a/exercises/22_clippy/clippy2.rs +++ b/exercises/22_clippy/clippy2.rs @@ -1,8 +1,3 @@ -// clippy2.rs -// -// Execute `rustlings hint clippy2` or use the `hint` watch subcommand for a -// hint. - fn main() { let mut res = 42; let option = Some(12); diff --git a/exercises/22_clippy/clippy3.rs b/exercises/22_clippy/clippy3.rs index 6a6a36b..fd829cf 100644 --- a/exercises/22_clippy/clippy3.rs +++ b/exercises/22_clippy/clippy3.rs @@ -1,7 +1,4 @@ -// clippy3.rs -// // Here's a couple more easy Clippy fixes, so you can see its utility. -// No hints. #[allow(unused_variables, unused_assignments)] fn main() { diff --git a/exercises/23_conversions/as_ref_mut.rs b/exercises/23_conversions/as_ref_mut.rs index 6fb7c2f..c725dfd 100644 --- a/exercises/23_conversions/as_ref_mut.rs +++ b/exercises/23_conversions/as_ref_mut.rs @@ -1,11 +1,6 @@ -// as_ref_mut.rs -// // AsRef and AsMut allow for cheap reference-to-reference conversions. Read more // about them at https://doc.rust-lang.org/std/convert/trait.AsRef.html and // https://doc.rust-lang.org/std/convert/trait.AsMut.html, respectively. -// -// Execute `rustlings hint as_ref_mut` or use the `hint` watch subcommand for a -// hint. // Obtain the number of bytes (not characters) in the given argument. // TODO: Add the AsRef trait appropriately as a trait bound. diff --git a/exercises/23_conversions/from_into.rs b/exercises/23_conversions/from_into.rs index d2a1609..9df10da 100644 --- a/exercises/23_conversions/from_into.rs +++ b/exercises/23_conversions/from_into.rs @@ -1,11 +1,6 @@ -// from_into.rs -// // The From trait is used for value-to-value conversions. If From is implemented // correctly for a type, the Into trait should work conversely. You can read // more about it at https://doc.rust-lang.org/std/convert/trait.From.html -// -// Execute `rustlings hint from_into` or use the `hint` watch subcommand for a -// hint. #[derive(Debug)] struct Person { @@ -24,7 +19,6 @@ impl Default for Person { } } - // Your task is to complete this implementation in order for the line `let p1 = // Person::from("Mark,20")` to compile. Please note that you'll need to parse the // age component into a `usize` with something like `"4".parse::()`. The diff --git a/exercises/23_conversions/from_str.rs b/exercises/23_conversions/from_str.rs index ed91ca5..58270f0 100644 --- a/exercises/23_conversions/from_str.rs +++ b/exercises/23_conversions/from_str.rs @@ -1,13 +1,8 @@ -// from_str.rs -// // This is similar to from_into.rs, but this time we'll implement `FromStr` and // return errors instead of falling back to a default value. Additionally, upon // implementing FromStr, you can use the `parse` method on strings to generate // an object of the implementor type. You can read more about it at // https://doc.rust-lang.org/std/str/trait.FromStr.html -// -// Execute `rustlings hint from_str` or use the `hint` watch subcommand for a -// hint. use std::num::ParseIntError; use std::str::FromStr; diff --git a/exercises/23_conversions/try_from_into.rs b/exercises/23_conversions/try_from_into.rs index 2316655..da45e5a 100644 --- a/exercises/23_conversions/try_from_into.rs +++ b/exercises/23_conversions/try_from_into.rs @@ -1,13 +1,8 @@ -// try_from_into.rs -// // TryFrom is a simple and safe type conversion that may fail in a controlled // way under some circumstances. Basically, this is the same as From. The main // difference is that this should return a Result type instead of the target // type itself. You can read more about it at // https://doc.rust-lang.org/std/convert/trait.TryFrom.html -// -// Execute `rustlings hint try_from_into` or use the `hint` watch subcommand for -// a hint. use std::convert::{TryFrom, TryInto}; diff --git a/exercises/23_conversions/using_as.rs b/exercises/23_conversions/using_as.rs index 9f617ec..94b1bb3 100644 --- a/exercises/23_conversions/using_as.rs +++ b/exercises/23_conversions/using_as.rs @@ -1,14 +1,9 @@ -// using_as.rs -// // Type casting in Rust is done via the usage of the `as` operator. Please note // that the `as` operator is not only used when type casting. It also helps with // renaming imports. // // The goal is to make sure that the division does not fail to compile and // returns the proper type. -// -// Execute `rustlings hint using_as` or use the `hint` watch subcommand for a -// hint. fn average(values: &[f64]) -> f64 { let total = values.iter().sum::(); diff --git a/exercises/quiz1.rs b/exercises/quiz1.rs index 55bc61f..edb672e 100644 --- a/exercises/quiz1.rs +++ b/exercises/quiz1.rs @@ -1,5 +1,3 @@ -// quiz1.rs -// // This is a quiz for the following sections: // - Variables // - Functions @@ -10,8 +8,6 @@ // - If Mary buys more than 40 apples, each apple only costs 1 rustbuck! // Write a function that calculates the price of an order of apples given the // quantity bought. -// -// No hints this time ;) // Put your function here! // fn calculate_price_of_apples { @@ -20,16 +16,21 @@ fn main() { // You can optionally experiment here. } -// Don't modify this function! -#[test] -fn verify_test() { - let price1 = calculate_price_of_apples(35); - let price2 = calculate_price_of_apples(40); - let price3 = calculate_price_of_apples(41); - let price4 = calculate_price_of_apples(65); +#[cfg(test)] +mod tests { + use super::*; + + // Don't modify this test! + #[test] + fn verify_test() { + let price1 = calculate_price_of_apples(35); + let price2 = calculate_price_of_apples(40); + let price3 = calculate_price_of_apples(41); + let price4 = calculate_price_of_apples(65); - assert_eq!(70, price1); - assert_eq!(80, price2); - assert_eq!(41, price3); - assert_eq!(65, price4); + assert_eq!(70, price1); + assert_eq!(80, price2); + assert_eq!(41, price3); + assert_eq!(65, price4); + } } diff --git a/exercises/quiz2.rs b/exercises/quiz2.rs index 1d73ab9..0a29e78 100644 --- a/exercises/quiz2.rs +++ b/exercises/quiz2.rs @@ -1,5 +1,3 @@ -// quiz2.rs -// // This is a quiz for the following sections: // - Strings // - Vecs @@ -17,8 +15,6 @@ // - The input is going to be a Vector of a 2-length tuple, // the first element is the string, the second one is the command. // - The output element is going to be a Vector of strings. -// -// No hints this time! pub enum Command { Uppercase, diff --git a/exercises/quiz3.rs b/exercises/quiz3.rs index 780e130..f255cb5 100644 --- a/exercises/quiz3.rs +++ b/exercises/quiz3.rs @@ -1,5 +1,3 @@ -// quiz3.rs -// // This quiz tests: // - Generics // - Traits @@ -13,8 +11,6 @@ // Make the necessary code changes in the struct ReportCard and the impl block // to support alphabetical report cards. Change the Grade in the second test to // "A+" to show that your changes allow alphabetical grades. -// -// Execute `rustlings hint quiz3` or use the `hint` watch subcommand for a hint. pub struct ReportCard { pub grade: f32, -- cgit v1.2.3 From de0befef9c780f9539458b21582f39f843d0bbc3 Mon Sep 17 00:00:00 2001 From: mo8it Date: Sat, 27 Apr 2024 23:37:17 +0200 Subject: Update intro1 --- exercises/00_intro/intro1.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'exercises/00_intro/intro1.rs') diff --git a/exercises/00_intro/intro1.rs b/exercises/00_intro/intro1.rs index 7000039..62bf95f 100644 --- a/exercises/00_intro/intro1.rs +++ b/exercises/00_intro/intro1.rs @@ -1,11 +1,10 @@ // 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, remove the `I AM NOT DONE` comment below. +// ready for the next exercise, enter `n` (or `next`) in the terminal. // -// If you're running this using `rustlings watch`: The exercise file will be -// reloaded when you change one of the lines below! Try adding a `println!` -// line, or try changing what it outputs in your terminal. Try removing a -// semicolon and see what happens! +// The exercise file will be reloaded when you change one of the lines below! +// Try adding a new `println!`. +// Try removing a semicolon and see what happens in the terminal! fn main() { println!("Hello and"); @@ -22,5 +21,6 @@ fn main() { println!("solve the exercises. Good luck!"); println!(); println!("The file of this exercise is `exercises/00_intro/intro1.rs`. Have a look!"); - println!("The current exercise path is shown under the progress bar in the watch mode."); + println!("The current exercise path will be always shown under the progress bar."); + println!("You can click on the path to open the exercise file in your editor."); } -- cgit v1.2.3 From d2b5906be226f936481ff3a5cb8fccde5c721524 Mon Sep 17 00:00:00 2001 From: mo8it Date: Mon, 13 May 2024 02:37:32 +0200 Subject: No more word input --- README.md | 4 ++-- exercises/00_intro/intro1.rs | 2 +- rustlings-macros/info.toml | 4 ++-- src/main.rs | 2 +- src/watch.rs | 2 +- src/watch/state.rs | 2 +- 6 files changed, 8 insertions(+), 8 deletions(-) (limited to 'exercises/00_intro/intro1.rs') diff --git a/README.md b/README.md index 3ba080f..0180608 100644 --- a/README.md +++ b/README.md @@ -98,7 +98,7 @@ It will rerun the current exercise automatically every time you change the exerc
If detecting file changes in the exercises/ directory fails… (click to expand) -> You can add the **`--manual-run`** flag (`rustlings --manual-run`) to manually rerun the current exercise by entering `r` (or `run`) in the watch mode. +> You can add the **`--manual-run`** flag (`rustlings --manual-run`) to manually rerun the current exercise by entering `r` in the watch mode. > > Please [report the issue](https://github.com/rust-lang/rustlings/issues/new) with some information about your operating system and whether you run Rustlings in a container or virtual machine (e.g. WSL). @@ -106,7 +106,7 @@ It will rerun the current exercise automatically every time you change the exerc ### Exercise List -In the [watch mode](#watch-mode) (after launching `rustlings`), you can enter `l` (or `list`) to open the interactive exercise list. +In the [watch mode](#watch-mode) (after launching `rustlings`), you can enter `l` to open the interactive exercise list. The list allows you to… diff --git a/exercises/00_intro/intro1.rs b/exercises/00_intro/intro1.rs index 62bf95f..bdbf34b 100644 --- a/exercises/00_intro/intro1.rs +++ b/exercises/00_intro/intro1.rs @@ -1,6 +1,6 @@ // 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, enter `n` (or `next`) in the terminal. +// ready for the next exercise, enter `n` in the terminal. // // The exercise file will be reloaded when you change one of the lines below! // Try adding a new `println!`. diff --git a/rustlings-macros/info.toml b/rustlings-macros/info.toml index 4204f27..485665e 100644 --- a/rustlings-macros/info.toml +++ b/rustlings-macros/info.toml @@ -13,7 +13,7 @@ get started, here are some notes about how Rustlings operates: the exercise file in your editor, fix errors and save the file. Rustlings will automatically detect the file change and rerun the exercise. If all errors are fixed, Rustlings will ask you to move on to the next exercise. -3. If you're stuck on an exercise, enter `h` (or `hint`) to show a hint. +3. If you're stuck on an exercise, enter `h` to show a hint. 4. If an exercise doesn't make sense to you, feel free to open an issue on GitHub! (https://github.com/rust-lang/rustlings). We look at every issue, and sometimes, other learners do too so you can help each other out! @@ -35,7 +35,7 @@ name = "intro1" dir = "00_intro" test = false # TODO: Fix hint -hint = """Enter `n` (or `next`) followed by ENTER to move on to the next exercise""" +hint = """Enter `n` to move on to the next exercise. You might need to press ENTER after typing `n`.""" [[exercises]] name = "intro2" diff --git a/src/main.rs b/src/main.rs index 15bcc8e..cf6f0d9 100644 --- a/src/main.rs +++ b/src/main.rs @@ -56,7 +56,7 @@ fn press_enter_prompt() -> io::Result<()> { struct Args { #[command(subcommand)] command: Option, - /// Manually run the current exercise using `r` or `run` in the watch mode. + /// Manually run the current exercise using `r` in the watch mode. /// Only use this if Rustlings fails to detect exercise file changes. #[arg(long)] manual_run: bool, diff --git a/src/watch.rs b/src/watch.rs index 7d4f54b..f72ebf7 100644 --- a/src/watch.rs +++ b/src/watch.rs @@ -123,5 +123,5 @@ The automatic detection of exercise file changes failed :( Please try running `rustlings` again. If you keep getting this error, run `rustlings --manual-run` to deactivate the file watcher. -You need to manually trigger running the current exercise using `r` (or `run`) then. +You need to manually trigger running the current exercise using `r` then. "; diff --git a/src/watch/state.rs b/src/watch/state.rs index 2e98546..c21d7ca 100644 --- a/src/watch/state.rs +++ b/src/watch/state.rs @@ -127,7 +127,7 @@ impl<'a> WatchState<'a> { self.writer, "{}\n", "Exercise done ✓ -When you are done experimenting, enter `n` (or `next`) to move on to the next exercise 🦀" +When you are done experimenting, enter `n` to move on to the next exercise 🦀" .bold() .green(), )?; -- cgit v1.2.3 From d3a0c269994eb2b11c0a3418e4db3a275a7ee5ad Mon Sep 17 00:00:00 2001 From: mo8it Date: Tue, 2 Jul 2024 16:26:28 +0200 Subject: Improve the placement of TODO comments --- exercises/00_intro/intro1.rs | 4 ++-- exercises/00_intro/intro2.rs | 3 +-- exercises/01_variables/variables4.rs | 1 - 3 files changed, 3 insertions(+), 5 deletions(-) (limited to 'exercises/00_intro/intro1.rs') diff --git a/exercises/00_intro/intro1.rs b/exercises/00_intro/intro1.rs index bdbf34b..22544cd 100644 --- a/exercises/00_intro/intro1.rs +++ b/exercises/00_intro/intro1.rs @@ -1,5 +1,5 @@ -// 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 +// TODO: 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, enter `n` in the terminal. // // The exercise file will be reloaded when you change one of the lines below! diff --git a/exercises/00_intro/intro2.rs b/exercises/00_intro/intro2.rs index e443ec8..c6cb645 100644 --- a/exercises/00_intro/intro2.rs +++ b/exercises/00_intro/intro2.rs @@ -1,5 +1,4 @@ -// TODO: Fix the code to print "Hello world!". - fn main() { + // TODO: Fix the code to print "Hello world!". printline!("Hello world!"); } diff --git a/exercises/01_variables/variables4.rs b/exercises/01_variables/variables4.rs index 8634ceb..6c138b1 100644 --- a/exercises/01_variables/variables4.rs +++ b/exercises/01_variables/variables4.rs @@ -1,5 +1,4 @@ // TODO: Fix the compiler error. - fn main() { let x = 3; println!("Number {x}"); -- cgit v1.2.3 From b8826dd3b31661927ad66d1bae2fd49f63e8d812 Mon Sep 17 00:00:00 2001 From: mo8it Date: Thu, 4 Jul 2024 13:08:59 +0200 Subject: Remove comment about removing a semicolon --- exercises/00_intro/intro1.rs | 1 - 1 file changed, 1 deletion(-) (limited to 'exercises/00_intro/intro1.rs') diff --git a/exercises/00_intro/intro1.rs b/exercises/00_intro/intro1.rs index 22544cd..21caff5 100644 --- a/exercises/00_intro/intro1.rs +++ b/exercises/00_intro/intro1.rs @@ -4,7 +4,6 @@ // // The exercise file will be reloaded when you change one of the lines below! // Try adding a new `println!`. -// Try removing a semicolon and see what happens in the terminal! fn main() { println!("Hello and"); -- cgit v1.2.3 From a4c07ca948dd57c71fe1894d05308af03304dec8 Mon Sep 17 00:00:00 2001 From: mo8it Date: Thu, 4 Jul 2024 13:10:18 +0200 Subject: Improve the comment in intro1 --- exercises/00_intro/intro1.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'exercises/00_intro/intro1.rs') diff --git a/exercises/00_intro/intro1.rs b/exercises/00_intro/intro1.rs index 21caff5..7b8baa2 100644 --- a/exercises/00_intro/intro1.rs +++ b/exercises/00_intro/intro1.rs @@ -3,7 +3,7 @@ // ready for the next exercise, enter `n` in the terminal. // // The exercise file will be reloaded when you change one of the lines below! -// Try adding a new `println!`. +// Try adding a new `println!` and check the updated output in the terminal. fn main() { println!("Hello and"); -- cgit v1.2.3