summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Cargo.lock58
-rw-r--r--Cargo.toml28
-rw-r--r--dev/Cargo.toml4
-rw-r--r--exercises/00_intro/intro1.rs5
-rw-r--r--exercises/00_intro/intro2.rs5
-rw-r--r--exercises/01_variables/variables1.rs5
-rw-r--r--exercises/01_variables/variables2.rs5
-rw-r--r--exercises/01_variables/variables3.rs5
-rw-r--r--exercises/01_variables/variables4.rs5
-rw-r--r--exercises/01_variables/variables5.rs5
-rw-r--r--exercises/01_variables/variables6.rs5
-rw-r--r--exercises/02_functions/functions1.rs5
-rw-r--r--exercises/02_functions/functions2.rs5
-rw-r--r--exercises/02_functions/functions3.rs5
-rw-r--r--exercises/02_functions/functions4.rs5
-rw-r--r--exercises/02_functions/functions5.rs5
-rw-r--r--exercises/03_if/if1.rs8
-rw-r--r--exercises/03_if/if2.rs8
-rw-r--r--exercises/03_if/if3.rs8
-rw-r--r--exercises/04_primitive_types/primitive_types1.rs5
-rw-r--r--exercises/04_primitive_types/primitive_types2.rs5
-rw-r--r--exercises/04_primitive_types/primitive_types3.rs5
-rw-r--r--exercises/04_primitive_types/primitive_types4.rs24
-rw-r--r--exercises/04_primitive_types/primitive_types5.rs5
-rw-r--r--exercises/04_primitive_types/primitive_types6.rs28
-rw-r--r--exercises/05_vecs/vecs1.rs8
-rw-r--r--exercises/05_vecs/vecs2.rs8
-rw-r--r--exercises/06_move_semantics/move_semantics1.rs30
-rw-r--r--exercises/06_move_semantics/move_semantics2.rs34
-rw-r--r--exercises/06_move_semantics/move_semantics3.rs30
-rw-r--r--exercises/06_move_semantics/move_semantics4.rs34
-rw-r--r--exercises/06_move_semantics/move_semantics5.rs30
-rw-r--r--exercises/06_move_semantics/move_semantics6.rs5
-rw-r--r--exercises/07_structs/structs1.rs9
-rw-r--r--exercises/07_structs/structs2.rs9
-rw-r--r--exercises/07_structs/structs3.rs9
-rw-r--r--exercises/08_enums/enums1.rs4
-rw-r--r--exercises/08_enums/enums2.rs5
-rw-r--r--exercises/08_enums/enums3.rs9
-rw-r--r--exercises/09_strings/strings1.rs5
-rw-r--r--exercises/09_strings/strings2.rs5
-rw-r--r--exercises/09_strings/strings3.rs9
-rw-r--r--exercises/09_strings/strings4.rs6
-rw-r--r--exercises/10_modules/modules1.rs5
-rw-r--r--exercises/10_modules/modules2.rs5
-rw-r--r--exercises/10_modules/modules3.rs5
-rw-r--r--exercises/11_hashmaps/hashmaps1.rs9
-rw-r--r--exercises/11_hashmaps/hashmaps2.rs11
-rw-r--r--exercises/11_hashmaps/hashmaps3.rs15
-rw-r--r--exercises/12_options/options1.rs9
-rw-r--r--exercises/12_options/options2.rs7
-rw-r--r--exercises/12_options/options3.rs5
-rw-r--r--exercises/13_error_handling/errors1.rs9
-rw-r--r--exercises/13_error_handling/errors2.rs9
-rw-r--r--exercises/13_error_handling/errors3.rs5
-rw-r--r--exercises/13_error_handling/errors4.rs30
-rw-r--r--exercises/13_error_handling/errors5.rs5
-rw-r--r--exercises/13_error_handling/errors6.rs9
-rw-r--r--exercises/14_generics/generics1.rs5
-rw-r--r--exercises/14_generics/generics2.rs9
-rw-r--r--exercises/15_traits/traits1.rs5
-rw-r--r--exercises/15_traits/traits2.rs8
-rw-r--r--exercises/15_traits/traits3.rs9
-rw-r--r--exercises/15_traits/traits4.rs9
-rw-r--r--exercises/15_traits/traits5.rs5
-rw-r--r--exercises/16_lifetimes/lifetimes1.rs5
-rw-r--r--exercises/16_lifetimes/lifetimes2.rs5
-rw-r--r--exercises/16_lifetimes/lifetimes3.rs10
-rw-r--r--exercises/17_tests/tests1.rs9
-rw-r--r--exercises/17_tests/tests2.rs9
-rw-r--r--exercises/17_tests/tests3.rs9
-rw-r--r--exercises/17_tests/tests4.rs13
-rw-r--r--exercises/18_iterators/iterators1.rs32
-rw-r--r--exercises/18_iterators/iterators2.rs9
-rw-r--r--exercises/18_iterators/iterators3.rs9
-rw-r--r--exercises/18_iterators/iterators4.rs10
-rw-r--r--exercises/18_iterators/iterators5.rs9
-rw-r--r--exercises/19_smart_pointers/arc1.rs4
-rw-r--r--exercises/19_smart_pointers/box1.rs4
-rw-r--r--exercises/19_smart_pointers/cow1.rs8
-rw-r--r--exercises/19_smart_pointers/rc1.rs109
-rw-r--r--exercises/20_threads/threads1.rs5
-rw-r--r--exercises/20_threads/threads2.rs5
-rw-r--r--exercises/20_threads/threads3.rs38
-rw-r--r--exercises/21_macros/macros1.rs5
-rw-r--r--exercises/21_macros/macros2.rs5
-rw-r--r--exercises/21_macros/macros3.rs5
-rw-r--r--exercises/21_macros/macros4.rs5
-rw-r--r--exercises/22_clippy/clippy1.rs5
-rw-r--r--exercises/22_clippy/clippy2.rs5
-rw-r--r--exercises/22_clippy/clippy3.rs3
-rw-r--r--exercises/23_conversions/as_ref_mut.rs9
-rw-r--r--exercises/23_conversions/from_into.rs6
-rw-r--r--exercises/23_conversions/from_str.rs5
-rw-r--r--exercises/23_conversions/try_from_into.rs5
-rw-r--r--exercises/23_conversions/using_as.rs5
-rw-r--r--exercises/quiz1.rs35
-rw-r--r--exercises/quiz2.rs8
-rw-r--r--exercises/quiz3.rs14
-rw-r--r--gen-dev-cargo-toml/Cargo.toml10
-rw-r--r--gen-dev-cargo-toml/src/main.rs68
-rw-r--r--info.toml2
-rw-r--r--rustlings-macros/Cargo.toml1
-rw-r--r--rustlings-macros/src/lib.rs11
-rw-r--r--src/app_state.rs79
-rw-r--r--src/dev.rs34
-rw-r--r--src/dev/check.rs214
-rw-r--r--src/dev/init.rs109
-rw-r--r--src/dev/update.rs53
-rw-r--r--src/embedded.rs6
-rw-r--r--src/exercise.rs28
-rw-r--r--src/info_file.rs9
-rw-r--r--src/init.rs89
-rw-r--r--src/list.rs4
-rw-r--r--src/list/state.rs13
-rw-r--r--src/main.rs99
-rw-r--r--src/watch.rs8
-rw-r--r--tests/dev_cargo_bins.rs44
-rw-r--r--tests/fixture/failure/info.toml2
-rw-r--r--tests/fixture/state/info.toml2
-rw-r--r--tests/fixture/success/info.toml2
121 files changed, 1042 insertions, 955 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 5cfebe6..b194de6 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -179,7 +179,7 @@ dependencies = [
"heck 0.5.0",
"proc-macro2",
"quote",
- "syn 2.0.58",
+ "syn",
]
[[package]]
@@ -312,15 +312,6 @@ dependencies = [
]
[[package]]
-name = "gen-dev-cargo-toml"
-version = "0.0.0"
-dependencies = [
- "anyhow",
- "serde",
- "toml_edit",
-]
-
-[[package]]
name = "hashbrown"
version = "0.14.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -589,9 +580,9 @@ dependencies = [
[[package]]
name = "proc-macro2"
-version = "1.0.79"
+version = "1.0.81"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e835ff2298f5721608eb1a980ecaee1aef2c132bf95ecc026a11b7bf3c01c02e"
+checksum = "3d1597b0c024618f09a9c3b8655b7e430397a36d23fdafec26d6965e9eec3eba"
dependencies = [
"unicode-ident",
]
@@ -607,9 +598,9 @@ dependencies = [
[[package]]
name = "ratatui"
-version = "0.26.1"
+version = "0.26.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bcb12f8fbf6c62614b0d56eb352af54f6a22410c3b079eb53ee93c7b97dd31d8"
+checksum = "a564a852040e82671dc50a37d88f3aa83bbc690dfc6844cfe7a2591620206a80"
dependencies = [
"bitflags 2.5.0",
"cassowary",
@@ -678,7 +669,7 @@ dependencies = [
[[package]]
name = "rustlings"
-version = "6.0.0"
+version = "6.0.0-alpha.0"
dependencies = [
"anyhow",
"assert_cmd",
@@ -696,7 +687,7 @@ dependencies = [
[[package]]
name = "rustlings-macros"
-version = "6.0.0"
+version = "6.0.0-alpha.0"
dependencies = [
"quote",
]
@@ -730,22 +721,22 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
[[package]]
name = "serde"
-version = "1.0.197"
+version = "1.0.198"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3fb1c873e1b9b056a4dc4c0c198b24c3ffa059243875552b2bd0933b1aee4ce2"
+checksum = "9846a40c979031340571da2545a4e5b7c4163bdae79b301d5f86d03979451fcc"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
-version = "1.0.197"
+version = "1.0.198"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7eb0b34b42edc17f6b7cac84a52a1c5f0e1bb2227e997ca9011ea3dd34e8610b"
+checksum = "e88edab869b01783ba905e7d0153f9fc1a6505a96e4ad3018011eedb838566d9"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.58",
+ "syn",
]
[[package]]
@@ -795,12 +786,12 @@ checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67"
[[package]]
name = "stability"
-version = "0.1.1"
+version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ebd1b177894da2a2d9120208c3386066af06a488255caabc5de8ddca22dbc3ce"
+checksum = "2ff9eaf853dec4c8802325d8b6d3dffa86cc707fd7a1a4cdbf416e13b061787a"
dependencies = [
"quote",
- "syn 1.0.109",
+ "syn",
]
[[package]]
@@ -834,25 +825,14 @@ dependencies = [
"proc-macro2",
"quote",
"rustversion",
- "syn 2.0.58",
-]
-
-[[package]]
-name = "syn"
-version = "1.0.109"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
-dependencies = [
- "proc-macro2",
- "quote",
- "unicode-ident",
+ "syn",
]
[[package]]
name = "syn"
-version = "2.0.58"
+version = "2.0.59"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "44cfb93f38070beee36b3fef7d4f5a16f27751d94b187b666a5cc5e9b0d30687"
+checksum = "4a6531ffc7b071655e4ce2e04bd464c4830bb585a61cabb96cf808f05172615a"
dependencies = [
"proc-macro2",
"quote",
@@ -1156,5 +1136,5 @@ checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.58",
+ "syn",
]
diff --git a/Cargo.toml b/Cargo.toml
index 07865ab..5e3be11 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -6,24 +6,17 @@ exclude = [
"tests/fixture/success",
"dev",
]
-members = [
- "gen-dev-cargo-toml",
-]
[workspace.package]
-version = "6.0.0"
+version = "6.0.0-alpha.0"
authors = [
"Liv <mokou@fastmail.com>",
"Carol (Nichols || Goulding) <carol.nichols@gmail.com>",
+ "Mo <mo8it@proton.me>",
]
license = "MIT"
edition = "2021"
-[workspace.dependencies]
-anyhow = "1.0.82"
-serde = { version = "1.0.197", features = ["derive"] }
-toml_edit = { version = "0.22.9", default-features = false, features = ["parse", "serde"] }
-
[package]
name = "rustlings"
description = "Small exercises to get you used to reading and writing Rust code!"
@@ -32,17 +25,24 @@ version.workspace = true
authors.workspace = true
license.workspace = true
edition.workspace = true
+include = [
+ "/exercises/",
+ "/info.toml",
+ "/LICENSE",
+ "/README.md",
+ "/src/",
+]
[dependencies]
-anyhow.workspace = true
+anyhow = "1.0.82"
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" }
-serde.workspace = true
-toml_edit.workspace = true
+ratatui = "0.26.2"
+rustlings-macros = { path = "rustlings-macros", version = "6.0.0-alpha.0" }
+serde = { version = "1.0.198", features = ["derive"] }
+toml_edit = { version = "0.22.9", default-features = false, features = ["parse", "serde"] }
which = "6.0.1"
[dev-dependencies]
diff --git a/dev/Cargo.toml b/dev/Cargo.toml
index 1d230eb..e66973e 100644
--- a/dev/Cargo.toml
+++ b/dev/Cargo.toml
@@ -1,6 +1,4 @@
-# This file is a hack to allow using `cargo run` to test `rustlings` during development.
-# You shouldn't edit it manually. It is created and updated by running `cargo run -p gen-dev-cargo-toml`.
-
+# Don't edit the `bin` list manually! It is updated by `cargo run -- dev update`
bin = [
{ name = "intro1", path = "../exercises/00_intro/intro1.rs" },
{ name = "intro2", path = "../exercises/00_intro/intro2.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 a1df66b..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.
@@ -10,6 +6,10 @@ pub fn bigger(a: i32, b: i32) -> i32 {
// - additional variables
}
+fn main() {
+ // You can optionally experiment here.
+}
+
// Don't mind this for now :)
#[cfg(test)]
mod tests {
diff --git a/exercises/03_if/if2.rs b/exercises/03_if/if2.rs
index 7b9c05f..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" {
@@ -13,6 +9,10 @@ pub fn foo_if_fizz(fizzish: &str) -> &str {
}
}
+fn main() {
+ // You can optionally experiment here.
+}
+
// No test changes needed!
#[cfg(test)]
mod tests {
diff --git a/exercises/03_if/if3.rs b/exercises/03_if/if3.rs
index caba172..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
@@ -27,6 +23,10 @@ pub fn animal_habitat(animal: &str) -> &'static str {
habitat
}
+fn main() {
+ // You can optionally experiment here.
+}
+
// No test changes needed.
#[cfg(test)]
mod tests {
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 8ed0a82..c583ae1 100644
--- a/exercises/04_primitive_types/primitive_types4.rs
+++ b/exercises/04_primitive_types/primitive_types4.rs
@@ -1,15 +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.
-#[test]
-fn slice_out_of_array() {
- let a = [1, 2, 3, 4, 5];
+fn main() {
+ // You can optionally experiment here.
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ #[test]
+ fn slice_out_of_array() {
+ let a = [1, 2, 3, 4, 5];
- let nice_slice = ???
+ let nice_slice = ???
- assert_eq!([2, 3, 4], 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 5f82f10..83cec24 100644
--- a/exercises/04_primitive_types/primitive_types6.rs
+++ b/exercises/04_primitive_types/primitive_types6.rs
@@ -1,17 +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.
-#[test]
-fn indexing_tuple() {
- let numbers = (1, 2, 3);
- // Replace below ??? with the tuple indexing syntax.
- let second = ???;
+fn main() {
+ // You can optionally experiment here.
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ #[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!")
+ 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 c64acbb..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<i32>) {
let a = [10, 20, 30, 40]; // a plain array
@@ -14,6 +10,10 @@ fn array_and_vec() -> ([i32; 4], Vec<i32>) {
(a, v)
}
+fn main() {
+ // You can optionally experiment here.
+}
+
#[cfg(test)]
mod tests {
use super::*;
diff --git a/exercises/05_vecs/vecs2.rs b/exercises/05_vecs/vecs2.rs
index d64d3d1..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<i32>) -> Vec<i32> {
for element in v.iter_mut() {
@@ -26,6 +22,10 @@ fn vec_map(v: &Vec<i32>) -> Vec<i32> {
}).collect()
}
+fn main() {
+ // You can optionally experiment here.
+}
+
#[cfg(test)]
mod tests {
use super::*;
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<i32>) -> Vec<i32> {
+ 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<i32>) -> Vec<i32> {
- 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<i32>) -> Vec<i32> {
let mut vec = vec;
@@ -22,3 +7,22 @@ fn fill_vec(vec: Vec<i32>) -> Vec<i32> {
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<i32>) -> Vec<i32> {
+ 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<i32>) -> Vec<i32> {
- 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<i32>` as argument - don't change this!
fn fill_vec() -> Vec<i32> {
@@ -25,3 +11,21 @@ fn fill_vec() -> Vec<i32> {
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 2978121..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
@@ -14,6 +9,10 @@ struct ColorTupleStruct(/* TODO: Something goes here */);
#[derive(Debug)]
struct UnitLikeStruct;
+fn main() {
+ // You can optionally experiment here.
+}
+
#[cfg(test)]
mod tests {
use super::*;
diff --git a/exercises/07_structs/structs2.rs b/exercises/07_structs/structs2.rs
index a7a2dec..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 {
@@ -28,6 +23,10 @@ fn create_order_template() -> Order {
}
}
+fn main() {
+ // You can optionally experiment here.
+}
+
#[cfg(test)]
mod tests {
use super::*;
diff --git a/exercises/07_structs/structs3.rs b/exercises/07_structs/structs3.rs
index 9835b81..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 {
@@ -38,6 +33,10 @@ impl Package {
}
}
+fn main() {
+ // You can optionally experiment here.
+}
+
#[cfg(test)]
mod tests {
use super::*;
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 580a553..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
@@ -45,6 +40,10 @@ impl State {
}
}
+fn main() {
+ // You can optionally experiment here.
+}
+
#[cfg(test)]
mod tests {
use super::*;
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 dedc081..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!
???
@@ -18,6 +13,10 @@ fn replace_me(input: &str) -> String {
???
}
+fn main() {
+ // You can optionally experiment here.
+}
+
#[cfg(test)]
mod tests {
use super::*;
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 5a52f61..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;
@@ -24,6 +19,10 @@ fn fruit_basket() -> HashMap<String, u32> {
basket
}
+fn main() {
+ // You can optionally experiment here.
+}
+
#[cfg(test)]
mod tests {
use super::*;
diff --git a/exercises/11_hashmaps/hashmaps2.rs b/exercises/11_hashmaps/hashmaps2.rs
index 2730643..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;
@@ -41,6 +36,10 @@ fn fruit_basket(basket: &mut HashMap<Fruit, u32>) {
}
}
+fn main() {
+ // You can optionally experiment here.
+}
+
#[cfg(test)]
mod tests {
use super::*;
@@ -79,7 +78,7 @@ mod tests {
let count = basket.values().sum::<u32>();
assert!(count > 11);
}
-
+
#[test]
fn all_fruit_types_in_basket() {
let mut basket = get_fruit_basket();
diff --git a/exercises/11_hashmaps/hashmaps3.rs b/exercises/11_hashmaps/hashmaps3.rs
index 775a401..070c370 100644
--- a/exercises/11_hashmaps/hashmaps3.rs
+++ b/exercises/11_hashmaps/hashmaps3.rs
@@ -1,19 +1,14 @@
-// hashmaps3.rs
-//
// A list of scores (one per line) of a soccer match is given. Each line is of
// the form : "<team_1_name>,<team_2_name>,<team_1_goals>,<team_2_goals>"
// Example: England,France,4,2 (England scored 4 goals, France 2).
//
// You have to build a scores table containing the name of the team, the total
-// number of goals the team scored, and the total number of goals the team
-// conceded. One approach to build the scores table is to use a Hashmap.
-// The solution is partially written to use a Hashmap,
+// number of goals the team scored, and the total number of goals the team
+// conceded. One approach to build the scores table is to use a Hashmap.
+// The solution is partially written to use a Hashmap,
// 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;
@@ -42,6 +37,10 @@ fn build_scores_table(results: String) -> HashMap<String, Team> {
scores
}
+fn main() {
+ // You can optionally experiment here.
+}
+
#[cfg(test)]
mod tests {
use super::*;
diff --git a/exercises/12_options/options1.rs b/exercises/12_options/options1.rs
index ba4b1cd..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 :(
@@ -14,6 +9,10 @@ fn maybe_icecream(time_of_day: u16) -> Option<u16> {
???
}
+fn main() {
+ // You can optionally experiment here.
+}
+
#[cfg(test)]
mod tests {
use super::*;
diff --git a/exercises/12_options/options2.rs b/exercises/12_options/options2.rs
index 73f707e..01f84c5 100644
--- a/exercises/12_options/options2.rs
+++ b/exercises/12_options/options2.rs
@@ -1,7 +1,6 @@
-// options2.rs
-//
-// Execute `rustlings hint options2` or use the `hint` watch subcommand for a
-// hint.
+fn main() {
+ // You can optionally experiment here.
+}
#[cfg(test)]
mod tests {
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 9767f2c..15a3716 100644
--- a/exercises/13_error_handling/errors1.rs
+++ b/exercises/13_error_handling/errors1.rs
@@ -1,13 +1,12 @@
-// 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.
+}
pub fn generate_nametag_text(name: String) -> Option<String> {
if name.is_empty() {
diff --git a/exercises/13_error_handling/errors2.rs b/exercises/13_error_handling/errors2.rs
index 88d1bf4..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;
@@ -29,6 +24,10 @@ pub fn total_cost(item_quantity: &str) -> Result<i32, ParseIntError> {
Ok(qty * cost_per_item + processing_fee)
}
+fn main() {
+ // You can optionally experiment here.
+}
+
#[cfg(test)]
mod tests {
use super::*;
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 0e5c08b..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);
@@ -19,12 +14,21 @@ impl PositiveNonzeroInteger {
}
}
-#[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));
+fn main() {
+ // You can optionally experiment here.
+}
+
+#[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 de73a9a..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<dyn error::Error>` 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;
@@ -54,6 +49,10 @@ impl PositiveNonzeroInteger {
}
}
+fn main() {
+ // You can optionally experiment here.
+}
+
#[cfg(test)]
mod test {
use super::*;
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 d50ed17..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,
@@ -16,6 +11,10 @@ impl Wrapper {
}
}
+fn main() {
+ // You can optionally experiment here.
+}
+
#[cfg(test)]
mod tests {
use super::*;
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 9a2bc07..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;
@@ -14,6 +10,10 @@ trait AppendBar {
// TODO: Implement trait `AppendBar` for a vector of strings.
+fn main() {
+ // You can optionally experiment here.
+}
+
#[cfg(test)]
mod tests {
use super::*;
diff --git a/exercises/15_traits/traits3.rs b/exercises/15_traits/traits3.rs
index 357f1d7..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;
@@ -23,6 +18,10 @@ struct OtherSoftware {
impl Licensed for SomeSoftware {} // Don't edit this line
impl Licensed for OtherSoftware {} // Don't edit this line
+fn main() {
+ // You can optionally experiment here.
+}
+
#[cfg(test)]
mod tests {
use super::*;
diff --git a/exercises/15_traits/traits4.rs b/exercises/15_traits/traits4.rs
index 7242c48..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 {
@@ -25,6 +20,10 @@ fn compare_license_types(software: ??, software_two: ??) -> bool {
software.licensing_info() == software_two.licensing_info()
}
+fn main() {
+ // You can optionally experiment here.
+}
+
#[cfg(test)]
mod tests {
use super::*;
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 bde2108..854a135 100644
--- a/exercises/17_tests/tests1.rs
+++ b/exercises/17_tests/tests1.rs
@@ -1,14 +1,13 @@
-// 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.
+}
#[cfg(test)]
mod tests {
diff --git a/exercises/17_tests/tests2.rs b/exercises/17_tests/tests2.rs
index aea5c0e..f0899e1 100644
--- a/exercises/17_tests/tests2.rs
+++ b/exercises/17_tests/tests2.rs
@@ -1,10 +1,9 @@
-// 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.
+}
#[cfg(test)]
mod tests {
diff --git a/exercises/17_tests/tests3.rs b/exercises/17_tests/tests3.rs
index d815e05..3b4e199 100644
--- a/exercises/17_tests/tests3.rs
+++ b/exercises/17_tests/tests3.rs
@@ -1,16 +1,15 @@
-// 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
}
+fn main() {
+ // You can optionally experiment here.
+}
+
#[cfg(test)]
mod tests {
use super::*;
diff --git a/exercises/17_tests/tests4.rs b/exercises/17_tests/tests4.rs
index 0972a5b..35a9a3b 100644
--- a/exercises/17_tests/tests4.rs
+++ b/exercises/17_tests/tests4.rs
@@ -1,13 +1,8 @@
-// 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,
- height: i32
+ height: i32,
}
impl Rectangle {
@@ -16,10 +11,14 @@ impl Rectangle {
if width <= 0 || height <= 0 {
panic!("Rectangle width and height cannot be negative!")
}
- Rectangle {width, height}
+ Rectangle { width, height }
}
}
+fn main() {
+ // You can optionally experiment here.
+}
+
#[cfg(test)]
mod tests {
use super::*;
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 4ca7742..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.
@@ -33,6 +28,10 @@ pub fn capitalize_words_string(words: &[&str]) -> String {
String::new()
}
+fn main() {
+ // You can optionally experiment here.
+}
+
#[cfg(test)]
mod tests {
use super::*;
diff --git a/exercises/18_iterators/iterators3.rs b/exercises/18_iterators/iterators3.rs
index f7da049..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 {
@@ -43,6 +38,10 @@ fn list_of_results() -> () {
let division_results = numbers.into_iter().map(|n| divide(n, 27));
}
+fn main() {
+ // You can optionally experiment here.
+}
+
#[cfg(test)]
mod tests {
use super::*;
diff --git a/exercises/18_iterators/iterators4.rs b/exercises/18_iterators/iterators4.rs
index af3958c..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,10 @@ pub fn factorial(num: u64) -> u64 {
// - additional variables
// For an extra challenge, don't use:
// - recursion
- // Execute `rustlings hint iterators4` for hints.
+}
+
+fn main() {
+ // You can optionally experiment here.
}
#[cfg(test)]
diff --git a/exercises/18_iterators/iterators5.rs b/exercises/18_iterators/iterators5.rs
index ceec536..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;
@@ -55,6 +50,10 @@ fn count_collection_iterator(collection: &[HashMap<String, Progress>], value: Pr
todo!();
}
+fn main() {
+ // You can optionally experiment here.
+}
+
#[cfg(test)]
mod tests {
use super::*;
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 b24591b..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;
@@ -25,6 +21,10 @@ fn abs_all<'a, 'b>(input: &'a mut Cow<'b, [i32]>) -> &'a mut Cow<'b, [i32]> {
input
}
+fn main() {
+ // You can optionally experiment here.
+}
+
#[cfg(test)]
mod tests {
use super::*;
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<T> 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<u32>) -> () {
});
}
-#[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 cd2c93b..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.
@@ -26,6 +21,10 @@ fn num_sq<T>(arg: &mut T) {
???
}
+fn main() {
+ // You can optionally experiment here.
+}
+
#[cfg(test)]
mod tests {
use super::*;
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::<usize>()`. 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::<f64>();
diff --git a/exercises/quiz1.rs b/exercises/quiz1.rs
index b9e71f5..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,22 +8,29 @@
// - 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 {
-// 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);
+fn main() {
+ // You can optionally experiment here.
+}
+
+#[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 8ace3fe..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,
@@ -40,6 +36,10 @@ mod my_module {
}
}
+fn main() {
+ // You can optionally experiment here.
+}
+
#[cfg(test)]
mod tests {
// TODO: What do we need to import to have `transformer` in scope?
diff --git a/exercises/quiz3.rs b/exercises/quiz3.rs
index 24f7082..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,
@@ -24,11 +20,17 @@ pub struct ReportCard {
impl ReportCard {
pub fn print(&self) -> String {
- format!("{} ({}) - achieved a grade of {}",
- &self.student_name, &self.student_age, &self.grade)
+ format!(
+ "{} ({}) - achieved a grade of {}",
+ &self.student_name, &self.student_age, &self.grade
+ )
}
}
+fn main() {
+ // You can optionally experiment here.
+}
+
#[cfg(test)]
mod tests {
use super::*;
diff --git a/gen-dev-cargo-toml/Cargo.toml b/gen-dev-cargo-toml/Cargo.toml
deleted file mode 100644
index 8922ae8..0000000
--- a/gen-dev-cargo-toml/Cargo.toml
+++ /dev/null
@@ -1,10 +0,0 @@
-[package]
-name = "gen-dev-cargo-toml"
-publish = false
-license.workspace = true
-edition.workspace = true
-
-[dependencies]
-anyhow.workspace = true
-serde.workspace = true
-toml_edit.workspace = true
diff --git a/gen-dev-cargo-toml/src/main.rs b/gen-dev-cargo-toml/src/main.rs
deleted file mode 100644
index 43b4ebd..0000000
--- a/gen-dev-cargo-toml/src/main.rs
+++ /dev/null
@@ -1,68 +0,0 @@
-// Generates `dev/Cargo.toml` such that it is synced with `info.toml`.
-// `dev/Cargo.toml` is a hack to allow using `cargo run` to test `rustlings`
-// during development.
-
-use anyhow::{bail, Context, Result};
-use serde::Deserialize;
-use std::{
- fs::{self, create_dir},
- io::ErrorKind,
-};
-
-#[derive(Deserialize)]
-struct ExerciseInfo {
- name: String,
- dir: Option<String>,
-}
-
-#[derive(Deserialize)]
-struct InfoFile {
- exercises: Vec<ExerciseInfo>,
-}
-
-fn main() -> Result<()> {
- let exercise_infos = toml_edit::de::from_str::<InfoFile>(
- &fs::read_to_string("info.toml").context("Failed to read `info.toml`")?,
- )
- .context("Failed to deserialize `info.toml`")?
- .exercises;
-
- let mut buf = Vec::with_capacity(1 << 14);
-
- buf.extend_from_slice(
- b"# This file is a hack to allow using `cargo run` to test `rustlings` during development.
-# You shouldn't edit it manually. It is created and updated by running `cargo run -p gen-dev-cargo-toml`.
-
-bin = [\n",
- );
-
- for exercise_info in exercise_infos {
- buf.extend_from_slice(b" { name = \"");
- buf.extend_from_slice(exercise_info.name.as_bytes());
- buf.extend_from_slice(b"\", path = \"../exercises/");
- if let Some(dir) = &exercise_info.dir {
- buf.extend_from_slice(dir.as_bytes());
- buf.push(b'/');
- }
- buf.extend_from_slice(exercise_info.name.as_bytes());
- buf.extend_from_slice(b".rs\" },\n");
- }
-
- buf.extend_from_slice(
- br#"]
-
-[package]
-name = "rustlings-dev"
-edition = "2021"
-publish = false
-"#,
- );
-
- if let Err(e) = create_dir("dev") {
- if e.kind() != ErrorKind::AlreadyExists {
- bail!("Failed to create the `dev` directory: {e}");
- }
- }
-
- fs::write("dev/Cargo.toml", buf).context("Failed to write `dev/Cargo.toml`")
-}
diff --git a/info.toml b/info.toml
index fa90ad7..27071a5 100644
--- a/info.toml
+++ b/info.toml
@@ -1,3 +1,5 @@
+format_version = 1
+
welcome_message = """Is this your first time? Don't worry, Rustlings was made for beginners! We are
going to teach you a lot of things about Rust, but before we can get
started, here's a couple of notes about how Rustlings operates:
diff --git a/rustlings-macros/Cargo.toml b/rustlings-macros/Cargo.toml
index 79279f5..095deaa 100644
--- a/rustlings-macros/Cargo.toml
+++ b/rustlings-macros/Cargo.toml
@@ -1,5 +1,6 @@
[package]
name = "rustlings-macros"
+description = "A macros crate intended to be only used by rustlings"
version.workspace = true
authors.workspace = true
license.workspace = true
diff --git a/rustlings-macros/src/lib.rs b/rustlings-macros/src/lib.rs
index d8da666..d95a93a 100644
--- a/rustlings-macros/src/lib.rs
+++ b/rustlings-macros/src/lib.rs
@@ -15,8 +15,8 @@ pub fn include_files(_: TokenStream) -> TokenStream {
let mut files = Vec::with_capacity(8);
let mut dirs = Vec::with_capacity(128);
- for entry in read_dir("exercises").expect("Failed to open the exercises directory") {
- let entry = entry.expect("Failed to read the exercises directory");
+ for entry in read_dir("exercises").expect("Failed to open the `exercises` directory") {
+ let entry = entry.expect("Failed to read the `exercises` directory");
if entry.file_type().unwrap().is_file() {
let path = entry.path();
@@ -46,13 +46,6 @@ pub fn include_files(_: TokenStream) -> TokenStream {
return None;
}
- if path.extension() != Some("rs".as_ref()) {
- panic!(
- "Found {} but expected only README.md and .rs files",
- path.display(),
- );
- }
-
Some(path_to_string(path))
});
diff --git a/src/app_state.rs b/src/app_state.rs
index 54c02d6..9868781 100644
--- a/src/app_state.rs
+++ b/src/app_state.rs
@@ -7,9 +7,15 @@ use crossterm::{
use std::{
fs::{self, File},
io::{Read, StdoutLock, Write},
+ path::Path,
+ process::{Command, Stdio},
};
-use crate::{exercise::Exercise, info_file::ExerciseInfo, FENISH_LINE};
+use crate::{
+ embedded::{WriteStrategy, EMBEDDED_FILES},
+ exercise::Exercise,
+ info_file::ExerciseInfo,
+};
const STATE_FILE_NAME: &str = ".rustlings-state.txt";
const BAD_INDEX_ERR: &str = "The current exercise index is higher than the number of exercises";
@@ -31,6 +37,7 @@ pub struct AppState {
n_done: u16,
final_message: String,
file_buf: Vec<u8>,
+ official_exercises: bool,
}
impl AppState {
@@ -111,6 +118,7 @@ impl AppState {
n_done: 0,
final_message,
file_buf: Vec::with_capacity(2048),
+ official_exercises: !Path::new("info.toml").exists(),
};
let state_file_status = slf.update_from_file();
@@ -172,6 +180,53 @@ impl AppState {
Ok(())
}
+ fn reset_path(&self, path: &str) -> Result<()> {
+ if self.official_exercises {
+ return EMBEDDED_FILES
+ .write_exercise_to_disk(path, WriteStrategy::Overwrite)
+ .with_context(|| format!("Failed to reset the exercise {path}"));
+ }
+
+ let output = Command::new("git")
+ .arg("stash")
+ .arg("push")
+ .arg("--")
+ .arg(path)
+ .stdin(Stdio::null())
+ .stdout(Stdio::null())
+ .output()
+ .with_context(|| format!("Failed to run `git stash push -- {path}`"))?;
+
+ if !output.status.success() {
+ bail!(
+ "`git stash push -- {path}` didn't run successfully: {}",
+ String::from_utf8_lossy(&output.stderr),
+ );
+ }
+
+ Ok(())
+ }
+
+ pub fn reset_current_exercise(&mut self) -> Result<&'static str> {
+ let path = self.current_exercise().path;
+ self.set_pending(self.current_exercise_ind)?;
+ self.reset_path(path)?;
+
+ Ok(path)
+ }
+
+ pub fn reset_exercise_by_ind(&mut self, exercise_ind: usize) -> Result<&'static str> {
+ if exercise_ind >= self.exercises.len() {
+ bail!(BAD_INDEX_ERR);
+ }
+
+ let path = self.exercises[exercise_ind].path;
+ self.set_pending(exercise_ind)?;
+ self.reset_path(path)?;
+
+ Ok(path)
+ }
+
fn next_pending_exercise_ind(&self) -> Option<usize> {
if self.current_exercise_ind == self.exercises.len() - 1 {
// The last exercise is done.
@@ -275,3 +330,25 @@ All exercises seem to be done.
Recompiling and running all exercises to make sure that all of them are actually done.
";
+
+const FENISH_LINE: &str = "+----------------------------------------------------+
+| You made it to the Fe-nish line! |
++-------------------------- ------------------------+
+ \\/\x1b[31m
+ ▒▒ ▒▒▒▒▒▒▒▒ ▒▒▒▒▒▒▒▒ ▒▒
+ ▒▒▒▒ ▒▒ ▒▒ ▒▒ ▒▒ ▒▒ ▒▒ ▒▒▒▒
+ ▒▒▒▒ ▒▒ ▒▒ ▒▒ ▒▒ ▒▒ ▒▒▒▒
+ ░░▒▒▒▒░░▒▒ ▒▒ ▒▒ ▒▒ ▒▒░░▒▒▒▒
+ ▓▓▓▓▓▓▓▓ ▓▓ ▓▓██ ▓▓ ▓▓██ ▓▓ ▓▓▓▓▓▓▓▓
+ ▒▒▒▒ ▒▒ ████ ▒▒ ████ ▒▒░░ ▒▒▒▒
+ ▒▒ ▒▒▒▒▒▒ ▒▒▒▒▒▒ ▒▒▒▒▒▒ ▒▒
+ ▒▒▒▒▒▒▒▒▒▒▓▓▓▓▓▓▒▒▒▒▒▒▒▒▓▓▓▓▓▓▒▒▒▒▒▒▒▒
+ ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
+ ▒▒▒▒▒▒▒▒▒▒██▒▒▒▒▒▒██▒▒▒▒▒▒▒▒▒▒
+ ▒▒ ▒▒▒▒▒▒▒▒▒▒██████▒▒▒▒▒▒▒▒▒▒ ▒▒
+ ▒▒ ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒ ▒▒
+ ▒▒ ▒▒ ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒ ▒▒ ▒▒
+ ▒▒ ▒▒ ▒▒ ▒▒ ▒▒ ▒▒
+ ▒▒ ▒▒ ▒▒ ▒▒\x1b[0m
+
+";
diff --git a/src/dev.rs b/src/dev.rs
new file mode 100644
index 0000000..1430f11
--- /dev/null
+++ b/src/dev.rs
@@ -0,0 +1,34 @@
+use anyhow::{bail, Context, Result};
+use clap::Subcommand;
+
+use crate::DEVELOPING_OFFICIAL_RUSTLINGS;
+
+mod check;
+mod init;
+mod update;
+
+#[derive(Subcommand)]
+pub enum DevCommands {
+ Init,
+ Check,
+ Update,
+}
+
+impl DevCommands {
+ pub fn run(self) -> Result<()> {
+ match self {
+ DevCommands::Init => {
+ if DEVELOPING_OFFICIAL_RUSTLINGS {
+ bail!("Disabled while developing the official Rustlings");
+ }
+
+ init::init().context(INIT_ERR)
+ }
+ DevCommands::Check => check::check(),
+ DevCommands::Update => update::update(),
+ }
+ }
+}
+
+const INIT_ERR: &str = "Initialization failed.
+After resolving the issue, delete the `rustlings` directory (if it was created) and try again";
diff --git a/src/dev/check.rs b/src/dev/check.rs
new file mode 100644
index 0000000..e95eb3c
--- /dev/null
+++ b/src/dev/check.rs
@@ -0,0 +1,214 @@
+use anyhow::{anyhow, bail, Context, Error, Result};
+use std::{
+ cmp::Ordering,
+ fs::{self, read_dir, OpenOptions},
+ io::Read,
+ path::{Path, PathBuf},
+};
+
+use crate::{
+ info_file::{ExerciseInfo, InfoFile},
+ CURRENT_FORMAT_VERSION, DEVELOPING_OFFICIAL_RUSTLINGS,
+};
+
+fn forbidden_char(input: &str) -> Option<char> {
+ input.chars().find(|c| *c != '_' && !c.is_alphanumeric())
+}
+
+fn check_info_file_exercises(info_file: &InfoFile) -> Result<hashbrown::HashSet<PathBuf>> {
+ let mut names = hashbrown::HashSet::with_capacity(info_file.exercises.len());
+ let mut paths = hashbrown::HashSet::with_capacity(info_file.exercises.len());
+
+ let mut file_buf = String::with_capacity(1 << 14);
+ for exercise_info in &info_file.exercises {
+ if exercise_info.name.is_empty() {
+ bail!("Found an empty exercise name in `info.toml`");
+ }
+ if let Some(c) = forbidden_char(&exercise_info.name) {
+ bail!(
+ "Char `{c}` in the exercise name `{}` is not allowed",
+ exercise_info.name,
+ );
+ }
+
+ if let Some(dir) = &exercise_info.dir {
+ if dir.is_empty() {
+ bail!(
+ "The exercise `{}` has an empty dir name in `info.toml`",
+ exercise_info.name,
+ );
+ }
+ if let Some(c) = forbidden_char(dir) {
+ bail!("Char `{c}` in the exercise dir `{dir}` is not allowed");
+ }
+ }
+
+ if exercise_info.hint.trim().is_empty() {
+ bail!("The exercise `{}` has an empty hint. Please provide a hint or at least tell the user why a hint isn't needed for this exercise", exercise_info.name);
+ }
+
+ if !names.insert(exercise_info.name.as_str()) {
+ bail!(
+ "The exercise name `{}` is duplicated. Exercise names must all be unique",
+ exercise_info.name,
+ );
+ }
+
+ let path = exercise_info.path();
+
+ OpenOptions::new()
+ .read(true)
+ .open(&path)
+ .with_context(|| format!("Failed to open the file {path}"))?
+ .read_to_string(&mut file_buf)
+ .with_context(|| format!("Failed to read the file {path}"))?;
+
+ if !file_buf.contains("fn main()") {
+ bail!("The `main` function is missing in the file `{path}`.\nCreate at least an empty `main` function to avoid language server errors");
+ }
+
+ file_buf.clear();
+
+ paths.insert(PathBuf::from(path));
+ }
+
+ Ok(paths)
+}
+
+fn unexpected_file(path: &Path) -> Error {
+ anyhow!("Found the file `{}`. Only `README.md` and Rust files related to an exercise in `info.toml` are allowed in the `exercises` directory", path.display())
+}
+
+fn check_exercise_dir_files(info_file_paths: &hashbrown::HashSet<PathBuf>) -> Result<()> {
+ for entry in read_dir("exercises").context("Failed to open the `exercises` directory")? {
+ let entry = entry.context("Failed to read the `exercises` directory")?;
+
+ if entry.file_type().unwrap().is_file() {
+ let path = entry.path();
+ let file_name = path.file_name().unwrap();
+ if file_name == "README.md" {
+ continue;
+ }
+
+ if !info_file_paths.contains(&path) {
+ return Err(unexpected_file(&path));
+ }
+
+ continue;
+ }
+
+ let dir_path = entry.path();
+ for entry in read_dir(&dir_path)
+ .with_context(|| format!("Failed to open the directory {}", dir_path.display()))?
+ {
+ let entry = entry
+ .with_context(|| format!("Failed to read the directory {}", dir_path.display()))?;
+ let path = entry.path();
+
+ if !entry.file_type().unwrap().is_file() {
+ bail!("Found `{}` but expected only files. Only one level of exercise nesting is allowed", path.display());
+ }
+
+ let file_name = path.file_name().unwrap();
+ if file_name == "README.md" {
+ continue;
+ }
+
+ if !info_file_paths.contains(&path) {
+ return Err(unexpected_file(&path));
+ }
+ }
+ }
+
+ Ok(())
+}
+
+fn check_exercises(info_file: &InfoFile) -> Result<()> {
+ match info_file.format_version.cmp(&CURRENT_FORMAT_VERSION) {
+ Ordering::Less => bail!("`format_version` < {CURRENT_FORMAT_VERSION} (supported version)\nPlease migrate to the latest format version"),
+ Ordering::Greater => bail!("`format_version` > {CURRENT_FORMAT_VERSION} (supported version)\nTry updating the Rustlings program"),
+ Ordering::Equal => (),
+ }
+
+ let info_file_paths = check_info_file_exercises(info_file)?;
+ check_exercise_dir_files(&info_file_paths)?;
+
+ Ok(())
+}
+
+pub fn bins_start_end_ind(cargo_toml: &str) -> Result<(usize, usize)> {
+ let start_ind = cargo_toml
+ .find("bin = [")
+ .context("Failed to find the start of the `bin` list (`bin = [`)")?
+ + 7;
+ let end_ind = start_ind
+ + cargo_toml
+ .get(start_ind..)
+ .and_then(|slice| slice.as_bytes().iter().position(|c| *c == b']'))
+ .context("Failed to find the end of the `bin` list (`]`)")?;
+
+ Ok((start_ind, end_ind))
+}
+
+pub fn append_bins(
+ buf: &mut Vec<u8>,
+ exercise_infos: &[ExerciseInfo],
+ exercise_path_prefix: &[u8],
+) {
+ buf.push(b'\n');
+ for exercise_info in exercise_infos {
+ buf.extend_from_slice(b" { name = \"");
+ buf.extend_from_slice(exercise_info.name.as_bytes());
+ buf.extend_from_slice(b"\", path = \"");
+ buf.extend_from_slice(exercise_path_prefix);
+ buf.extend_from_slice(b"exercises/");
+ if let Some(dir) = &exercise_info.dir {
+ buf.extend_from_slice(dir.as_bytes());
+ buf.push(b'/');
+ }
+ buf.extend_from_slice(exercise_info.name.as_bytes());
+ buf.extend_from_slice(b".rs\" },\n");
+ }
+}
+
+fn check_cargo_toml(
+ exercise_infos: &[ExerciseInfo],
+ current_cargo_toml: &str,
+ exercise_path_prefix: &[u8],
+) -> Result<()> {
+ let (bins_start_ind, bins_end_ind) = bins_start_end_ind(current_cargo_toml)?;
+
+ let old_bins = &current_cargo_toml.as_bytes()[bins_start_ind..bins_end_ind];
+ let mut new_bins = Vec::with_capacity(1 << 13);
+ append_bins(&mut new_bins, exercise_infos, exercise_path_prefix);
+
+ if old_bins != new_bins {
+ bail!("`Cargo.toml` is outdated. Run `rustlings dev update` to update it");
+ }
+
+ Ok(())
+}
+
+pub fn check() -> Result<()> {
+ let info_file = InfoFile::parse()?;
+ check_exercises(&info_file)?;
+
+ if DEVELOPING_OFFICIAL_RUSTLINGS {
+ check_cargo_toml(
+ &info_file.exercises,
+ include_str!("../../dev/Cargo.toml"),
+ b"../",
+ )
+ .context("The file `dev/Cargo.toml` is outdated. Please run `cargo run -- dev update` to update it")?;
+ } else {
+ let current_cargo_toml =
+ fs::read_to_string("Cargo.toml").context("Failed to read the file `Cargo.toml`")?;
+ check_cargo_toml(&info_file.exercises, &current_cargo_toml, b"").context(
+ "The file `Cargo.toml` is outdated. Please run `rustlings dev update` to update it",
+ )?;
+ }
+
+ println!("\nEverything looks fine!");
+
+ Ok(())
+}
diff --git a/src/dev/init.rs b/src/dev/init.rs
new file mode 100644
index 0000000..3ce5055
--- /dev/null
+++ b/src/dev/init.rs
@@ -0,0 +1,109 @@
+use anyhow::{Context, Result};
+use std::fs::{self, create_dir};
+
+use crate::CURRENT_FORMAT_VERSION;
+
+pub fn init() -> Result<()> {
+ create_dir("rustlings").context("Failed to create the directory `rustlings`")?;
+
+ create_dir("rustlings/exercises")
+ .context("Failed to create the directory `rustlings/exercises`")?;
+
+ create_dir("rustlings/solutions")
+ .context("Failed to create the directory `rustlings/solutions`")?;
+
+ fs::write(
+ "rustlings/info.toml",
+ format!("{INFO_FILE_BEFORE_FORMAT_VERSION}{CURRENT_FORMAT_VERSION}{INFO_FILE_AFTER_FORMAT_VERSION}"),
+ )
+ .context("Failed to create the file `rustlings/info.toml`")?;
+
+ fs::write("rustlings/Cargo.toml", CARGO_TOML)
+ .context("Failed to create the file `rustlings/Cargo.toml`")?;
+
+ fs::write("rustlings/.gitignore", crate::init::GITIGNORE)
+ .context("Failed to create the file `rustlings/.gitignore`")?;
+
+ fs::write("rustlings/README.md", README)
+ .context("Failed to create the file `rustlings/README.md`")?;
+
+ create_dir("rustlings/.vscode")
+ .context("Failed to create the directory `rustlings/.vscode`")?;
+ fs::write(
+ "rustlings/.vscode/extensions.json",
+ crate::init::VS_CODE_EXTENSIONS_JSON,
+ )
+ .context("Failed to create the file `rustlings/.vscode/extensions.json`")?;
+
+ println!("{INIT_DONE}");
+
+ Ok(())
+}
+
+const INFO_FILE_BEFORE_FORMAT_VERSION: &str =
+ "# The format version is an indicator of the compatibility of third-party exercises with the
+# Rustlings program.
+# The format version is not the same as the version of the Rustlings program.
+# In case Rustlings makes an unavoidable breaking change to the expected format of third-party
+# exercises, you would need to raise this version and adapt to the new format.
+# Otherwise, the newest version of the Rustlings program won't be able to run these exercises.
+format_version = ";
+
+const INFO_FILE_AFTER_FORMAT_VERSION: &str = r#"
+
+# Optional multi-line message to be shown to users when just starting with the exercises.
+welcome_message = """Welcome to these third-party Rustlings exercises."""
+
+# Optional multi-line message to be shown to users after finishing all exercises.
+final_message = """We hope that you found the exercises helpful :D"""
+
+# Repeat this section for every exercise.
+[[exercises]]
+# Exercise name which is the exercise file name without the `.rs` extension.
+name = "???"
+
+# Optional directory name to be provided if you want to organize exercises in directories.
+# If `dir` is specified, the exercise path is `exercises/DIR/NAME.rs`
+# Otherwise, the path is `exercises/NAME.rs`
+# dir = "???"
+
+# The mode to run the exercise in.
+# The mode "test" (preferred) runs the exercise's tests.
+# The mode "run" only checks if the exercise compiles and runs it.
+mode = "test"
+
+# A multi-line hint to be shown to users on request.
+hint = """???"""
+"#;
+
+const CARGO_TOML: &[u8] =
+ br#"# Don't edit the `bin` list manually! It is updated by `rustlings dev update`
+bin = []
+
+[package]
+name = "rustlings"
+edition = "2021"
+publish = false
+
+[dependencies]
+"#;
+
+const README: &str = "# Rustlings 🦀
+
+Welcome to these third-party Rustlings exercises 😃
+
+First,
+[install Rustlings using the official instructions in the README of the Rustlings project](https://github.com/rust-lang/rustlings) ✅
+
+Then, open your terminal in this directory and run `rustlings` to get started with the exercises 🚀
+";
+
+const INIT_DONE: &str = r#"Initialization done!
+You can start developing third-party Rustlings exercises in the `rustlings` directory :D
+
+If the initialization was done in a Rust project which is a Cargo workspace, you need to add the
+path to the `rustlings` directory to the `workspace.exclude` list in the project's `Cargo.toml`
+file. For example:
+
+[workspace]
+exclude = ["rustlings"]"#;
diff --git a/src/dev/update.rs b/src/dev/update.rs
new file mode 100644
index 0000000..65dcf76
--- /dev/null
+++ b/src/dev/update.rs
@@ -0,0 +1,53 @@
+use std::fs;
+
+use anyhow::{Context, Result};
+
+use crate::{
+ info_file::{ExerciseInfo, InfoFile},
+ DEVELOPING_OFFICIAL_RUSTLINGS,
+};
+
+use super::check::{append_bins, bins_start_end_ind};
+
+fn update_cargo_toml(
+ exercise_infos: &[ExerciseInfo],
+ current_cargo_toml: &str,
+ cargo_toml_path: &str,
+ exercise_path_prefix: &[u8],
+) -> Result<()> {
+ let (bins_start_ind, bins_end_ind) = bins_start_end_ind(current_cargo_toml)?;
+
+ let mut new_cargo_toml = Vec::with_capacity(1 << 13);
+ new_cargo_toml.extend_from_slice(current_cargo_toml[..bins_start_ind].as_bytes());
+ append_bins(&mut new_cargo_toml, exercise_infos, exercise_path_prefix);
+ new_cargo_toml.extend_from_slice(current_cargo_toml[bins_end_ind..].as_bytes());
+
+ fs::write(cargo_toml_path, new_cargo_toml).context("Failed to write the `Cargo.toml` file")?;
+
+ Ok(())
+}
+
+pub fn update() -> Result<()> {
+ let info_file = InfoFile::parse()?;
+
+ if DEVELOPING_OFFICIAL_RUSTLINGS {
+ update_cargo_toml(
+ &info_file.exercises,
+ include_str!("../../dev/Cargo.toml"),
+ "dev/Cargo.toml",
+ b"../",
+ )
+ .context("Failed to update the file `dev/Cargo.toml`")?;
+
+ println!("Updated `dev/Cargo.toml`");
+ } else {
+ let current_cargo_toml =
+ fs::read_to_string("Cargo.toml").context("Failed to read the file `Cargo.toml`")?;
+ update_cargo_toml(&info_file.exercises, &current_cargo_toml, "Cargo.toml", b"")
+ .context("Failed to update the file `Cargo.toml`")?;
+
+ println!("Updated `Cargo.toml`");
+ }
+
+ Ok(())
+}
diff --git a/src/embedded.rs b/src/embedded.rs
index 866b12b..eae3099 100644
--- a/src/embedded.rs
+++ b/src/embedded.rs
@@ -47,14 +47,12 @@ impl EmbeddedFlatDir {
let path = Path::new(self.path);
if let Err(e) = create_dir(path) {
- if !path.is_dir() {
+ if e.kind() != io::ErrorKind::AlreadyExists {
return Err(e);
}
}
- self.readme.write_to_disk(WriteStrategy::Overwrite)?;
-
- Ok(())
+ self.readme.write_to_disk(WriteStrategy::Overwrite)
}
}
diff --git a/src/exercise.rs b/src/exercise.rs
index 2ec8d97..eb7b725 100644
--- a/src/exercise.rs
+++ b/src/exercise.rs
@@ -3,13 +3,11 @@ use crossterm::style::{style, StyledContent, Stylize};
use std::{
fmt::{self, Display, Formatter},
fs,
+ path::Path,
process::{Command, Output},
};
-use crate::{
- embedded::{WriteStrategy, EMBEDDED_FILES},
- info_file::Mode,
-};
+use crate::{info_file::Mode, DEVELOPING_OFFICIAL_RUSTLINGS};
pub struct TerminalFileLink<'a> {
path: &'a str,
@@ -50,9 +48,7 @@ impl Exercise {
cmd.arg(command);
// A hack to make `cargo run` work when developing Rustlings.
- // Use `dev/Cargo.toml` when in the directory of the repository.
- #[cfg(debug_assertions)]
- if std::path::Path::new("tests").exists() {
+ if DEVELOPING_OFFICIAL_RUSTLINGS && Path::new("tests").exists() {
cmd.arg("--manifest-path").arg("dev/Cargo.toml");
}
@@ -69,7 +65,17 @@ impl Exercise {
pub fn run(&self) -> Result<Output> {
match self.mode {
Mode::Run => self.cargo_cmd("run", &[]),
- Mode::Test => self.cargo_cmd("test", &["--", "--nocapture", "--format", "pretty"]),
+ Mode::Test => self.cargo_cmd(
+ "test",
+ &[
+ "--",
+ "--color",
+ "always",
+ "--nocapture",
+ "--format",
+ "pretty",
+ ],
+ ),
Mode::Clippy => self.cargo_cmd(
"clippy",
&["--", "-D", "warnings", "-D", "clippy::float_cmp"],
@@ -77,12 +83,6 @@ impl Exercise {
}
}
- pub fn reset(&self) -> Result<()> {
- EMBEDDED_FILES
- .write_exercise_to_disk(self.path, WriteStrategy::Overwrite)
- .with_context(|| format!("Failed to reset the exercise {self}"))
- }
-
pub fn terminal_link(&self) -> StyledContent<TerminalFileLink<'_>> {
style(TerminalFileLink { path: self.path })
.underlined()
diff --git a/src/info_file.rs b/src/info_file.rs
index 2a45e02..879609e 100644
--- a/src/info_file.rs
+++ b/src/info_file.rs
@@ -39,6 +39,7 @@ impl ExerciseInfo {
#[derive(Deserialize)]
pub struct InfoFile {
+ pub format_version: u8,
pub welcome_message: Option<String>,
pub final_message: Option<String>,
pub exercises: Vec<ExerciseInfo>,
@@ -63,14 +64,6 @@ impl InfoFile {
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)
}
}
diff --git a/src/init.rs b/src/init.rs
index 459519d..52315e2 100644
--- a/src/init.rs
+++ b/src/init.rs
@@ -1,64 +1,24 @@
use anyhow::{bail, Context, Result};
use std::{
env::set_current_dir,
- fs::{create_dir, OpenOptions},
- io::{self, ErrorKind, Write},
+ fs::{self, create_dir},
+ io::ErrorKind,
path::Path,
};
-use crate::{embedded::EMBEDDED_FILES, info_file::ExerciseInfo};
-
-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_info in exercise_infos {
- cargo_toml.extend_from_slice(b" { name = \"");
- 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.push(b'/');
- }
- cargo_toml.extend_from_slice(exercise_info.name.as_bytes());
- cargo_toml.extend_from_slice(b".rs\" },\n");
- }
-
- cargo_toml.extend_from_slice(
- br#"]
-
-[package]
-name = "rustlings"
-edition = "2021"
-publish = false
-"#,
- );
- OpenOptions::new()
- .create_new(true)
- .write(true)
- .open("Cargo.toml")?
- .write_all(&cargo_toml)
-}
-
-fn create_gitignore() -> io::Result<()> {
- OpenOptions::new()
- .create_new(true)
- .write(true)
- .open(".gitignore")?
- .write_all(GITIGNORE)
-}
-
-fn create_vscode_dir() -> Result<()> {
- create_dir(".vscode").context("Failed to create the directory `.vscode`")?;
- OpenOptions::new()
- .create_new(true)
- .write(true)
- .open(".vscode/extensions.json")?
- .write_all(VS_CODE_EXTENSIONS_JSON)?;
+use crate::embedded::EMBEDDED_FILES;
- Ok(())
-}
+const CARGO_TOML: &[u8] = {
+ let cargo_toml = include_bytes!("../dev/Cargo.toml");
+ // Skip the first line (comment).
+ let mut start_ind = 0;
+ while cargo_toml[start_ind] != b'\n' {
+ start_ind += 1;
+ }
+ cargo_toml.split_at(start_ind + 1).1
+};
-pub fn init(exercise_infos: &[ExerciseInfo]) -> Result<()> {
+pub fn init() -> Result<()> {
if Path::new("exercises").is_dir() && Path::new("Cargo.toml").is_file() {
bail!(PROBABLY_IN_RUSTLINGS_DIR_ERR);
}
@@ -78,21 +38,27 @@ pub fn init(exercise_infos: &[ExerciseInfo]) -> Result<()> {
.init_exercises_dir()
.context("Failed to initialize the `rustlings/exercises` directory")?;
- create_cargo_toml(exercise_infos)
+ fs::write("Cargo.toml", CARGO_TOML)
.context("Failed to create the file `rustlings/Cargo.toml`")?;
- create_gitignore().context("Failed to create the file `rustlings/.gitignore`")?;
+ fs::write(".gitignore", GITIGNORE)
+ .context("Failed to create the file `rustlings/.gitignore`")?;
- create_vscode_dir().context("Failed to create the file `rustlings/.vscode/extensions.json`")?;
+ create_dir(".vscode").context("Failed to create the directory `rustlings/.vscode`")?;
+ fs::write(".vscode/extensions.json", VS_CODE_EXTENSIONS_JSON)
+ .context("Failed to create the file `rustlings/.vscode/extensions.json`")?;
+
+ println!("{POST_INIT_MSG}");
Ok(())
}
-const GITIGNORE: &[u8] = b"/target
-/.rustlings-state.txt
+pub const GITIGNORE: &[u8] = b"Cargo.lock
+.rustlings-state.txt
+target
";
-const VS_CODE_EXTENSIONS_JSON: &[u8] = br#"{"recommendations":["rust-lang.rust-analyzer"]}"#;
+pub const VS_CODE_EXTENSIONS_JSON: &[u8] = br#"{"recommendations":["rust-lang.rust-analyzer"]}"#;
const PROBABLY_IN_RUSTLINGS_DIR_ERR: &str =
"A directory with the name `exercises` and a file with the name `Cargo.toml` already exist
@@ -106,3 +72,8 @@ const RUSTLINGS_DIR_ALREADY_EXISTS_ERR: &str =
You probably already initialized Rustlings.
Run `cd rustlings`
Then run `rustlings` again";
+
+const POST_INIT_MSG: &str = "Done initialization!
+
+Run `cd rustlings` to go into the generated directory.
+Then run `rustlings` to get started.";
diff --git a/src/list.rs b/src/list.rs
index 2bb813d..790c02f 100644
--- a/src/list.rs
+++ b/src/list.rs
@@ -7,12 +7,12 @@ use crossterm::{
use ratatui::{backend::CrosstermBackend, Terminal};
use std::io;
-mod state;
-
use crate::app_state::AppState;
use self::state::{Filter, UiState};
+mod state;
+
pub fn list(app_state: &mut AppState) -> Result<()> {
let mut stdout = io::stdout().lock();
stdout.execute(EnterAlternateScreen)?;
diff --git a/src/list/state.rs b/src/list/state.rs
index 2a1fef1..0253bb9 100644
--- a/src/list/state.rs
+++ b/src/list/state.rs
@@ -217,23 +217,22 @@ impl<'a> UiState<'a> {
return Ok(self);
};
- let (ind, exercise) = self
+ let ind = self
.app_state
.exercises()
.iter()
.enumerate()
.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)),
+ Filter::Done => exercise.done.then_some(ind),
+ Filter::Pending => (!exercise.done).then_some(ind),
+ Filter::None => Some(ind),
})
.nth(selected)
.context("Invalid selection index")?;
- exercise.reset()?;
+ let exercise_path = self.app_state.reset_exercise_by_ind(ind)?;
self.message
- .write_fmt(format_args!("The exercise {exercise} has been reset!"))?;
- self.app_state.set_pending(ind)?;
+ .write_fmt(format_args!("The exercise {exercise_path} has been reset"))?;
Ok(self.with_updated_rows())
}
diff --git a/src/main.rs b/src/main.rs
index ed5becf..0469737 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -1,4 +1,4 @@
-use anyhow::{Context, Result};
+use anyhow::{bail, Context, Result};
use app_state::StateFileStatus;
use clap::{Parser, Subcommand};
use crossterm::{
@@ -11,7 +11,10 @@ use std::{
process::exit,
};
+use self::{app_state::AppState, dev::DevCommands, info_file::InfoFile, watch::WatchExit};
+
mod app_state;
+mod dev;
mod embedded;
mod exercise;
mod info_file;
@@ -21,13 +24,17 @@ mod progress_bar;
mod run;
mod watch;
-use self::{
- app_state::AppState,
- info_file::InfoFile,
- init::init,
- list::list,
- run::run,
- watch::{watch, WatchExit},
+const CURRENT_FORMAT_VERSION: u8 = 1;
+const DEVELOPING_OFFICIAL_RUSTLINGS: bool = {
+ #[allow(unused_assignments, unused_mut)]
+ let mut debug_profile = false;
+
+ #[cfg(debug_assertions)]
+ {
+ debug_profile = true;
+ }
+
+ debug_profile
};
/// Rustlings is a collection of small exercises to get you used to writing and reading Rust code
@@ -61,6 +68,8 @@ enum Subcommands {
/// The name of the exercise
name: String,
},
+ #[command(subcommand)]
+ Dev(DevCommands),
}
fn main() -> Result<()> {
@@ -68,18 +77,29 @@ fn main() -> Result<()> {
which::which("cargo").context(CARGO_NOT_FOUND_ERR)?;
- let info_file = InfoFile::parse()?;
+ match args.command {
+ Some(Subcommands::Init) => {
+ if DEVELOPING_OFFICIAL_RUSTLINGS {
+ bail!("Disabled while developing the official Rustlings");
+ }
- if matches!(args.command, Some(Subcommands::Init)) {
- init(&info_file.exercises).context("Initialization failed")?;
+ return init::init().context("Initialization failed");
+ }
+ Some(Subcommands::Dev(dev_command)) => return dev_command.run(),
+ _ => (),
+ }
- println!("{POST_INIT_MSG}");
- return Ok(());
- } else if !Path::new("exercises").is_dir() {
+ if !Path::new("exercises").is_dir() {
println!("{PRE_INIT_MSG}");
exit(1);
}
+ let info_file = InfoFile::parse()?;
+
+ if info_file.format_version > CURRENT_FORMAT_VERSION {
+ bail!(FORMAT_VERSION_HIGHER_ERR);
+ }
+
let (mut app_state, state_file_status) = AppState::new(
info_file.exercises,
info_file.final_message.unwrap_or_default(),
@@ -121,34 +141,32 @@ fn main() -> Result<()> {
};
loop {
- match watch(&mut app_state, notify_exercise_paths)? {
+ match watch::watch(&mut app_state, notify_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)?,
+ WatchExit::List => list::list(&mut app_state)?,
}
}
}
- // `Init` is handled above.
- Some(Subcommands::Init) => (),
Some(Subcommands::Run { name }) => {
if let Some(name) = name {
app_state.set_current_exercise_by_name(&name)?;
}
- run(&mut app_state)?;
+ run::run(&mut app_state)?;
}
Some(Subcommands::Reset { name }) => {
app_state.set_current_exercise_by_name(&name)?;
- let exercise = app_state.current_exercise();
- exercise.reset()?;
- println!("The exercise {exercise} has been reset!");
- app_state.set_pending(app_state.current_exercise_ind())?;
+ let exercise_path = app_state.reset_current_exercise()?;
+ println!("The exercise {exercise_path} has been reset");
}
Some(Subcommands::Hint { name }) => {
app_state.set_current_exercise_by_name(&name)?;
println!("{}", app_state.current_exercise().hint);
}
+ // Handled in an earlier match.
+ Some(Subcommands::Init | Subcommands::Dev(_)) => (),
}
Ok(())
@@ -158,8 +176,13 @@ const CARGO_NOT_FOUND_ERR: &str = "Failed to find `cargo`.
Did you already install Rust?
Try running `cargo --version` to diagnose the problem.";
+const FORMAT_VERSION_HIGHER_ERR: &str =
+ "The format version specified in the `info.toml` file is higher than the last one supported.
+It is possible that you have an outdated version of Rustlings.
+Try to install the latest Rustlings version first.";
+
const PRE_INIT_MSG: &str = r"
- welcome to...
+ Welcome to...
_ _ _
_ __ _ _ ___| |_| (_)_ __ __ _ ___
| '__| | | / __| __| | | '_ \ / _` / __|
@@ -169,31 +192,3 @@ const PRE_INIT_MSG: &str = r"
The `exercises` directory wasn't found in the current directory.
If you are just starting with Rustlings, run the command `rustlings init` to initialize it.";
-
-const POST_INIT_MSG: &str = "
-Done initialization!
-
-Run `cd rustlings` to go into the generated directory.
-Then run `rustlings` to get started.";
-
-const FENISH_LINE: &str = "+----------------------------------------------------+
-| You made it to the Fe-nish line! |
-+-------------------------- ------------------------+
- \\/\x1b[31m
- ▒▒ ▒▒▒▒▒▒▒▒ ▒▒▒▒▒▒▒▒ ▒▒
- ▒▒▒▒ ▒▒ ▒▒ ▒▒ ▒▒ ▒▒ ▒▒ ▒▒▒▒
- ▒▒▒▒ ▒▒ ▒▒ ▒▒ ▒▒ ▒▒ ▒▒▒▒
- ░░▒▒▒▒░░▒▒ ▒▒ ▒▒ ▒▒ ▒▒░░▒▒▒▒
- ▓▓▓▓▓▓▓▓ ▓▓ ▓▓██ ▓▓ ▓▓██ ▓▓ ▓▓▓▓▓▓▓▓
- ▒▒▒▒ ▒▒ ████ ▒▒ ████ ▒▒░░ ▒▒▒▒
- ▒▒ ▒▒▒▒▒▒ ▒▒▒▒▒▒ ▒▒▒▒▒▒ ▒▒
- ▒▒▒▒▒▒▒▒▒▒▓▓▓▓▓▓▒▒▒▒▒▒▒▒▓▓▓▓▓▓▒▒▒▒▒▒▒▒
- ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
- ▒▒▒▒▒▒▒▒▒▒██▒▒▒▒▒▒██▒▒▒▒▒▒▒▒▒▒
- ▒▒ ▒▒▒▒▒▒▒▒▒▒██████▒▒▒▒▒▒▒▒▒▒ ▒▒
- ▒▒ ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒ ▒▒
- ▒▒ ▒▒ ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒ ▒▒ ▒▒
- ▒▒ ▒▒ ▒▒ ▒▒ ▒▒ ▒▒
- ▒▒ ▒▒ ▒▒ ▒▒\x1b[0m
-
-";
diff --git a/src/watch.rs b/src/watch.rs
index d20e552..5ebe526 100644
--- a/src/watch.rs
+++ b/src/watch.rs
@@ -11,10 +11,6 @@ use std::{
time::Duration,
};
-mod notify_event;
-mod state;
-mod terminal_event;
-
use crate::app_state::{AppState, ExercisesProgress};
use self::{
@@ -23,6 +19,10 @@ use self::{
terminal_event::{terminal_event_handler, InputEvent},
};
+mod notify_event;
+mod state;
+mod terminal_event;
+
enum WatchEvent {
Input(InputEvent),
FileChange { exercise_ind: usize },
diff --git a/tests/dev_cargo_bins.rs b/tests/dev_cargo_bins.rs
deleted file mode 100644
index 81f48b1..0000000
--- a/tests/dev_cargo_bins.rs
+++ /dev/null
@@ -1,44 +0,0 @@
-// Makes sure that `dev/Cargo.toml` is synced with `info.toml`.
-// When this test fails, you just need to run `cargo run -p gen-dev-cargo-toml`.
-
-use serde::Deserialize;
-use std::fs;
-
-#[derive(Deserialize)]
-struct ExerciseInfo {
- name: String,
- dir: Option<String>,
-}
-
-#[derive(Deserialize)]
-struct InfoFile {
- exercises: Vec<ExerciseInfo>,
-}
-
-#[test]
-fn dev_cargo_bins() {
- let cargo_toml = fs::read_to_string("dev/Cargo.toml").unwrap();
-
- let exercise_infos =
- toml_edit::de::from_str::<InfoFile>(&fs::read_to_string("info.toml").unwrap())
- .unwrap()
- .exercises;
-
- let mut start_ind = 0;
- for exercise_info in exercise_infos {
- let name_start = start_ind + cargo_toml[start_ind..].find('"').unwrap() + 1;
- let name_end = name_start + cargo_toml[name_start..].find('"').unwrap();
- assert_eq!(exercise_info.name, &cargo_toml[name_start..name_end]);
-
- let path_start = name_end + cargo_toml[name_end + 1..].find('"').unwrap() + 2;
- let path_end = path_start + cargo_toml[path_start..].find('"').unwrap();
- let expected_path = if let Some(dir) = exercise_info.dir {
- format!("../exercises/{dir}/{}.rs", exercise_info.name)
- } else {
- format!("../exercises/{}.rs", exercise_info.name)
- };
- assert_eq!(expected_path, &cargo_toml[path_start..path_end]);
-
- start_ind = path_end + 1;
- }
-}
diff --git a/tests/fixture/failure/info.toml b/tests/fixture/failure/info.toml
index 94ec6ea..ef99a07 100644
--- a/tests/fixture/failure/info.toml
+++ b/tests/fixture/failure/info.toml
@@ -1,3 +1,5 @@
+format_version = 1
+
[[exercises]]
name = "compFailure"
mode = "run"
diff --git a/tests/fixture/state/info.toml b/tests/fixture/state/info.toml
index e5c4d8f..eec24ea 100644
--- a/tests/fixture/state/info.toml
+++ b/tests/fixture/state/info.toml
@@ -1,3 +1,5 @@
+format_version = 1
+
[[exercises]]
name = "pending_exercise"
mode = "run"
diff --git a/tests/fixture/success/info.toml b/tests/fixture/success/info.toml
index 674ba26..88650ec 100644
--- a/tests/fixture/success/info.toml
+++ b/tests/fixture/success/info.toml
@@ -1,3 +1,5 @@
+format_version = 1
+
[[exercises]]
name = "compSuccess"
mode = "run"